Bootstrap

在 Flutter 中如何使用TabBar和TabBarView创建选项卡布局【Flutter 专题 13】

本教程将向您展示如何使用和在 Flutter 中创建选项卡布局。

如果您的应用程序需要显示某些内容,将内容分成多个选项卡是很常见的。在 Flutter 中,借助和小部件可以轻松创建这样的布局。用于创建选项卡,而用于定义每个选项卡的内容。Flutter 已经处理了如何在选项卡之间切换,这对我们来说更容易。此外,Flutter 还可以自定义选项卡布局的样式和行为。下面是解释和例子。

基本用法

提供一个

和需要一个工作。有两种不同的方式来提供控制器。第一个是将 a作为祖先小部件。可以使用下面的构造函数创建。

const DefaultTabController({
    Key? key,
    required int length,
    int initialIndex = 0,
    required Widget child,
  })

该参数用于设置您要创建的选项卡数量。它必须是相同的长度和。否则,您可能会收到以下错误。

  The following assertion was thrown building TabBar(dirty, dependencies: [_LocalizationsScope-[GlobalKey#7d8f8], _TabControllerScope, _InheritedTheme], state: _TabBarState#0360c):
  Controller's length property (3) does not match the number of tabs (2) present in TabBar's tabs property.

然后,您需要传递一个小部件作为参数。在和小部件有(下面将被置为子控件的后代在树节点)。

MaterialApp(
    home: DefaultTabController(
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          bottom: TabBar(
            tabs: [
              // Add Tabs here
            ],
          ),
          title: const Text('坚果前端'),
          backgroundColor: Colors.teal,
        ),
        body: const TabBarView(
          physics: BouncingScrollPhysics(),
          dragStartBehavior: DragStartBehavior.down,
          children: [
            // Add widgets here
          ],
        ),
      ),
    ),
  )

提供一种控制器的另一种方式是通过使用的参数。它提供了更多的选择来控制和的行为,与配合使用。例如,您可以以编程方式触发控制器为特定选项卡设置动画。

  TabController({
    int initialIndex = 0,
    required int length,
    required TickerProvider vsync
  })

要创建自己的,您必须传递指示选项卡数量的参数。它需要的值是和TabBar.tabsTabBarView.childrenvsync with TickerProviderStateMixinStatethisvsync`参数的值。

 class _TabLayoutExampleState extends State with TickerProviderStateMixin {
  
    late TabController _tabController;
  
    @override
    void initState() {
      super.initState();
      _tabController = TabController(length: 6, vsync: this);
      _tabController.animateTo(2);
    }
  
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        home: Scaffold(
          appBar: AppBar(
            bottom: TabBar(
              controller: _tabController,
              tabs: [
                // Put Tabs here
              ],
            ),
            title: const Text('Woolha.com Flutter Tutorial'),
            backgroundColor: Colors.teal,
          ),
          body: TabBarView(
            controller: _tabController,
            children: [
              // Put widgets here
            ],
          ),
        ),
      );
    }
  }

创建

如果要实现选项卡布局,首先需要有一个包含选项卡列表的选项卡栏。在 Flutter 中,您可以使用小部件。的可以在任何地方根据设计被放置。如果你想把它放在 的正下方,你可以将它作为 的参数传递。下面是构造函数。

  TabBar({
    Key? key,
    required List tabs,
    TabController? controller,
    bool isScrollable,
    Color? indicatorColor,
    bool automaticIndicatorColorAdjustment,
    double indicatorWeight,
    EdgeInsetsGeometry indicatorPadding,
    Decoration? indicator,
    TabBarIndicatorSize? indicatorSize,
    Color? labelColor,
    TextStyle? labelStyle,
    EdgeInsetsGeometry? labelPadding,
    Color? unselectedLabelColor,
    TextStyle? unselectedLabelStyle,
    DragStartBehavior dragStartBehavior,
    MaterialStateProperty? overlayColor,
    MouseCursor? mouseCursor,
    bool? enableFeedback,
    ValueChanged? onTap,
    ScrollPhysics? physics
  })

构造函数有很多参数,但大多数都是可选的。唯一需要的参数是您需要为其传递小部件列表。对于要显示的每个选项卡,您需要创建一个小部件。小部件的数量必须与和 的长度相同。

创建

要创建 的实例,您可以使用下面的构造函数。

  const Tab({
    Key? key,
    String? text,
    Widget? icon,
    EdgeInsetsGeometry iconMargin = const EdgeInsets.only(bottom: 10.0),
    Widget? child,
  })

Flutter通过传递( )、( ) 或( ) 参数中的至少一个,使您可以灵活地定义 。因此,您可以选择其中之一,也可以将它们组合起来,但是,不允许同时通过和。这意味着您只能传递only、only、only、+和+ 。

  static const List _tabs = [
    const Tab(icon: Icon(Icons.looks_one), child: const Text('Tab One')),
    const Tab(icon: Icon(Icons.looks_two), text: 'Tab Two'),
    const Tab(icon: Icon(Icons.looks_3), text: 'Tab Three'),
  ];

s的列表需要作为 的参数传递。

  TabBar(    tabs: _tabs,  )

创建

除了,您还需要使用下面的构造函数创建。

  TabBarView({    Key? key,    required List children,    TabController? controller,    ScrollPhysics? physics,    DragStartBehavior dragStartBehavior  })

对于作为 的参数传递的每个,您需要定义其对应的视图。为此,您必须传递一个s列表作为参数。传递的小部件的顺序必须与选项卡的顺序相同。

  static const List _views = [    const Center(child: const Text('Content of Tab One')),    const Center(child: const Text('Content of Tab Two')),    const Center(child: const Text('Content of Tab Three')),  ];

需要作为传递部件列表的说法。

  TabBarView(    children: _views,  )

正确创建和后,您应该有一个有效的选项卡布局。

定制

可选参数可用于自定义.

设置样式

您可以通过传递一个值作为参数来设置图标和文本的颜色。对于未选择的选项卡,您可以通过传递另一个作为参数来为图标和文本设置不同的颜色。您还可以通过传递参数来设置。它还提供可用于设置未选择选项卡的样式。对于设置颜色,您不能使用和参数,即使可以使用 a 来定义颜色。为此,您必须使用和参数。

  TabBar(    labelColor: Colors.red,    unselectedLabelColor: Colors.grey,    labelStyle: const TextStyle(fontWeight: FontWeight.bold),    unselectedLabelStyle: const TextStyle(fontStyle: FontStyle.italic),    tabs: _tabs,  )

输出:

仍然与颜色相关,小部件有一个参数,可用于定义选项卡处于聚焦、悬停和按下状态时的墨水颜色。您需要传递一个接受 a作为参数并根据当前状态返回 的函数。

  TabBar(    labelColor: Colors.red,    unselectedLabelColor: Colors.grey,    labelStyle: const TextStyle(fontWeight: FontWeight.bold),    unselectedLabelStyle: const TextStyle(fontStyle: FontStyle.italic),    overlayColor: MaterialStateColor.resolveWith((Set states) {      if (states.contains(MaterialState.pressed)) {        return Colors.blue;      } if (states.contains(MaterialState.focused)) {        return Colors.orange;      } else if (states.contains(MaterialState.hovered)) {        return Colors.pinkAccent;      }       return Colors.transparent;    }),    tabs: _tabs,  )

输出:

自定义指示器

在这种情况下,指示器是用于指示正在选择选项卡的布局的一部分。默认情况下,Flutter 会在所选选项卡的底部显示一个非常细的水平条。如果你想让它看起来更厚,你可以改变它的默认值为2.0。可以通过传递 a作为参数来更改指示器颜色。

指示器另一种自定义是通过将枚举值作为参数传递的大小。如果值为,指标将与选项卡一样宽。如果值为,则指示器的宽度取决于标签的宽度。也可以使用类型为的参数在指标周围添加填充。

  TabBar(    indicatorWeight: 10,    indicatorColor: Colors.red,    indicatorSize: TabBarIndicatorSize.tab,    indicatorPadding: const EdgeInsets.all(10),    tabs: _tabs,  )

输出:

如果您想为指示器使用完全不同的设计而不是选项卡底部的默认水平条,您可以将 作为指示器参数传递。如果您创建自定义指示器,则参数在传递后可能无效。

  TabBar(    indicatorPadding: const EdgeInsets.all(5),    indicator: BoxDecoration(      border: Border.all(color: Colors.red),      borderRadius: BorderRadius.circular(10),      color: Colors.pinkAccent,    ),    tabs: _tabs,  )

输出:

使标签可滚动

有时,选项卡可能很长并且不适合屏幕的宽度。默认情况下,Flutter 不会使其可滚动。结果,每个选项卡变得非常窄,某些部分被截断。该解决方案通过命名参数与作为值。

  TabBar(    isScrollable: true,    tabs: _tabs,  )

输出:

设置Physics

如果您使选项卡可滚动,您还可以设置用户滚动选项卡时的物理行为。为此,请传递一个值作为参数。下面的示例使用.

  TabBar(    isScrollable: true,    physics: BouncingScrollPhysics(),    tabs: _tabs,  )

输出:

选项卡上的Handle

当一个选项卡被按下时,Flutter 会自动切换到相应的. 如果您想在按下选项卡时触发另一件事,您可以传递一个回调函数作为参数。

  TabBar(    onTap: (int index) {      print('Tab $index is tapped');    },    tabs: _tabs,  )

启用反馈

要启用反馈,您可以传递参数并将值设置为。

  TabBar(    enableFeedback: true,    tabs: _tabs,  )

定制

的内容取决于您作为参数传递的小部件。因此,这取决于您如何创建小部件。除此之外,Flutter 还允许您自定义.

设置Physics

您可以通过将参数传递给 的构造函数来设置用户滚动 时的物理行为。传递的值必须是 。下面的示例使用.

  TabBarView(    physics: BouncingScrollPhysics(),    children: _views,  )

输出:

- 参数

  • :小部件的键,用于控制小部件如何替换为另一个小部件。

  • :标签的数量。

  • :所选标签的初始索引。默认为 0。

  • :树中此小部件下方的小部件,其中包含和。

?: 值可以为空。required:必须传递值。

- 参数

  • :标签的数量。

  • :所选标签的初始索引。默认为 0。

  • :使用。

required:必须传递值。

- 参数

  • :widget的key,用于控制widget被替换的方式

  • :选项卡列表。

  • :用于控制选择和动画状态。

  • : 这个标签栏是否可以水平滚动。默认为.

  • :所选选项卡下方线条的颜色。

  • :如果与父控件的颜色相同,此标签栏是否应自动调整为白色。默认为.

  • :所选选项卡下方线条的粗细。默认为 2.0。

  • :指标的填充。默认为.

  • : 用于创建自定义指标。

  • : 如何确定指标大小。

  • :所选标签标签的颜色。

  • :所选标签标签的文本样式。

  • :添加到每个选项卡标签的填充。

  • :未选中的标签标签的颜色。

  • :未选中的标签标签的文本样式。

  • :确定处理拖动开始行为的方式。默认为.

  • :定义墨水响应焦点、悬停和飞溅颜色。

  • :当指针悬停在选项卡上时鼠标光标..

  • :手势是否应提供声音和/或触觉反馈。

  • :点击选项卡时调用的回调。

  • :当用户交互时影响胺化的物理效果。

?: 值可以为空。required:必须传递值。

- 参数

  • :小部件的键,用于控制小部件如何替换为另一个小部件。

  • :每个选项卡的小部件。

  • :用于控制选择和动画状态。

  • :当用户交互时影响胺化的物理效果。

  • :确定处理拖动开始行为的方式。默认为.

?: 值可以为空。required:必须传递值。

完整代码

  import 'package:flutter/gestures.dart';  import 'package:flutter/material.dart';  import 'package:flutter/rendering.dart';    void main() => runApp(MyApp());    class MyApp extends StatelessWidget {      @override    Widget build(BuildContext context) {      return MaterialApp(        title: '坚果前端',        home: TabLayoutExample(),      );    }  }    class TabLayoutExample extends StatefulWidget {    @override    State createState() {      return _TabLayoutExampleState();    }    }    class _TabLayoutExampleState extends State with TickerProviderStateMixin {      late TabController _tabController;      @override    void initState() {      super.initState();      _tabController = TabController(length: 6, vsync: this);      _tabController.animateTo(2);    }      static const List _tabs = [      const Tab(icon: Icon(Icons.looks_one), child: const Text('Tab One')),      const Tab(icon: Icon(Icons.looks_two), text: 'Tab Two'),      const Tab(icon: Icon(Icons.looks_3), text: 'Tab Three'),      const Tab(icon: Icon(Icons.looks_4), text: 'Tab Four'),      const Tab(icon: Icon(Icons.looks_5), text: 'Tab Five'),      const Tab(icon: Icon(Icons.looks_6), text: 'Tab Six'),    ];      static const List _views = [      const Center(child: const Text('Content of Tab One')),      const Center(child: const Text('Content of Tab Two')),      const Center(child: const Text('Content of Tab Three')),      const Center(child: const Text('Content of Tab Four')),      const Center(child: const Text('Content of Tab Five')),      const Center(child: const Text('Content of Tab Six')),    ];      @override    Widget build(BuildContext context) {      return MaterialApp(        home: DefaultTabController(          length: 6,          child: Scaffold(            appBar: AppBar(              bottom: TabBar(                labelColor: Colors.white,                unselectedLabelColor: Colors.grey,                labelStyle: const TextStyle(fontWeight: FontWeight.bold),                unselectedLabelStyle: const TextStyle(fontStyle: FontStyle.italic),                overlayColor: MaterialStateColor.resolveWith((Set states) {                  if (states.contains(MaterialState.pressed)) {                    return Colors.blue;                  } if (states.contains(MaterialState.focused)) {                    return Colors.orange;                  } else if (states.contains(MaterialState.hovered)) {                    return Colors.pinkAccent;                  }                    return Colors.transparent;                }),                indicatorWeight: 10,                indicatorColor: Colors.red,                indicatorSize: TabBarIndicatorSize.tab,                indicatorPadding: const EdgeInsets.all(5),                indicator: BoxDecoration(                  border: Border.all(color: Colors.red),                  borderRadius: BorderRadius.circular(10),                  color: Colors.pinkAccent,                ),                isScrollable: true,                physics: BouncingScrollPhysics(),                onTap: (int index) {                  print('Tab $index is tapped');                },                enableFeedback: true,                // Uncomment the line below and remove DefaultTabController if you want to use a custom TabController                // controller: _tabController,                tabs: _tabs,              ),              title: const Text('Woolha.com Flutter Tutorial'),              backgroundColor: Colors.teal,            ),            body: const TabBarView(              physics: BouncingScrollPhysics(),              // Uncomment the line below and remove DefaultTabController if you want to use a custom TabController              // controller: _tabController,              children: _views,            ),          ),        ),      );    }  }

概括

这就是在 Flutter 中创建选项卡布局的方法。首先,您需要有一个. 然后,您需要创建一个包含选项卡列表的和一个包含每个选项卡的视图。可以通过传递可选参数来自定义选项卡的行为样式。

今天的内容到这儿就分享到这儿。不知道通过这种方式大家能否理解!