且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2022-06-09 19:50:37

基本用法

提供一个 TabController

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

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

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


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.

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

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
          ],
        ),
      ),
    ),
  )

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


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


要创建自己的TabController,您必须传递length指示选项卡数量的参数。它需要的值是和 TabBar.tabsTabBarView.children相同的长度。您必须传递的另一个必需参数是vsync. 您可以通过添加 with TickerProviderStateMixin到您的State类中来获取它,以便您可以使用this关键字作为vsync`参数的值。


 class _TabLayoutExampleState extends State<TabLayoutExample> 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
            ],
          ),
        ),
      );
    }
  }


创建 TabBar

  TabBar({
    Key? key,
    required List<Widget> 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<Color?>? overlayColor,
    MouseCursor? mouseCursor,
    bool? enableFeedback,
    ValueChanged<int>? onTap,
    ScrollPhysics? physics
  })

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

创建 Tab

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


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


FlutterTab通过传递text( String)、icon( Widget) 或child( Widget) 参数中的至少一个,使您可以灵活地定义 。因此,您可以选择其中之一,也可以将它们组合起来,但是,不允许同时通过textchild。这意味着您只能传递textonly、icononly、childonly、text+iconicon+ child


  static const List<Tab> _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'),
  ];


Tabs 的列表需要作为 的tabs参数传递TabBar


TabBar(    tabs: _tabs,  )


创建 TabBarView

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

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

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

  static const List<Widget> _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')),  ];

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


TabBarView(    children: _views,  )

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

定制 TabBar

可选参数TabBar可用于自定义TabBar.

设置样式

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

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

输出:


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


TabBar(    labelColor: Colors.red,    unselectedLabelColor: Colors.grey,    labelStyle: const TextStyle(fontWeight: FontWeight.bold),    unselectedLabelStyle: const TextStyle(fontStyle: FontStyle.italic),    overlayColor: MaterialStateColor.resolveWith((Set<MaterialState> 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 会在所选选项卡的底部显示一个非常细的水平条。如果你想让它看起来更厚,你可以改变indicatorWeight它的默认值为 2.0。可以通过传递 aColor作为indicatorColor参数来更改指示器颜色。


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


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


输出:


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


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


输出:

使标签可滚动

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

  TabBar(    isScrollable: true,    tabs: _tabs,  )

输出:

设置 Physics

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


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

复制代码


输出:

选项卡上的 Handle

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


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

复制代码

启用反馈

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


TabBar(    enableFeedback: true,    tabs: _tabs,  )

复制代码

定制 TabBarView

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

设置 Physics

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


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


输出:

DefaultTabController - 参数

  • Key? key:小部件的键,用于控制小部件如何替换为另一个小部件。
  • required int length:标签的数量。
  • int initialIndex:所选标签的初始索引。默认为 0。
  • required Widget child:树中此小部件下方的小部件,其中包含TabBarTabBarView


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

TabController - 参数

  • required int length:标签的数量。
  • int initialIndex:所选标签的初始索引。默认为 0。
  • required TickerProvider vsync:TickerProvider使用。


required:必须传递值。

TabBar - 参数

  • Key? key:widget 的 key,用于控制 widget 被替换的方式
  • required List<Widget> tabs:选项卡列表。
  • TabController? controller:用于控制选择和动画状态。
  • bool isScrollable: 这个标签栏是否可以水平滚动。默认为false.
  • Color? indicatorColor:所选选项卡下方线条的颜色。
  • bool automaticIndicatorColorAdjustment:indicatorColor如果与indicatorColor父控件的颜色相同,此标签栏是否应自动调整为白色。默认为true.
  • double indicatorWeight:所选选项卡下方线条的粗细。默认为 2.0。
  • EdgeInsetsGeometry indicatorPadding:指标的填充。默认为EdgeInsets.zero.
  • Decoration? indicator: 用于创建自定义指标。
  • TabBarIndicatorSize? indicatorSize: 如何确定指标大小。
  • Color? labelColor:所选标签标签的颜色。
  • TextStyle? labelStyle:所选标签标签的文本样式。
  • EdgeInsetsGeometry? labelPadding:添加到每个选项卡标签的填充。
  • Color? unselectedLabelColor:未选中的标签标签的颜色。
  • Color? unselectedLabelStyle:未选中的标签标签的文本样式。
  • DragStartBehavior dragStartBehavior:确定处理拖动开始行为的方式。默认为DragStartBehavior.start.
  • MaterialStateProperty<Color?>? overlayColor:定义墨水响应焦点、悬停和飞溅颜色。
  • MouseCursor? mouseCursor:当指针悬停在选项卡上时鼠标光标..
  • bool? enableFeedback:手势是否应提供声音和/或触觉反馈。
  • ValueChanged<int>? onTap:点击选项卡时调用的回调。
  • ScrollPhysics? physics:当用户交互时影响胺化的物理效果。


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

TabBarView - 参数

  • Key? key:小部件的键,用于控制小部件如何替换为另一个小部件。
  • required List<Widget> children:每个选项卡的小部件。
  • TabController? controller:用于控制选择和动画状态。
  • ScrollPhysics? physics:当用户交互时影响胺化的物理效果。
  • DragStartBehavior dragStartBehavior:确定处理拖动开始行为的方式。默认为DragStartBehavior.start.


?: 值可以为空。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<StatefulWidget> createState() {      return _TabLayoutExampleState();    }    }    class _TabLayoutExampleState extends State<TabLayoutExample> with TickerProviderStateMixin {      late TabController _tabController;      @override    void initState() {      super.initState();      _tabController = TabController(length: 6, vsync: this);      _tabController.animateTo(2);    }      static const List<Tab> _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<Widget> _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<MaterialState> 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 中创建选项卡布局的方法。首先,您需要有一个TabController. 然后,您需要创建一个TabBar包含选项卡列表的和一个TabBarView包含每个选项卡的视图。可以通过传递可选参数来自定义选项卡的行为样式。


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