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

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

基本用法

提供一个 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包含每个选项卡的视图。可以通过传递可选参数来自定义选项卡的行为样式。


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

相关文章
|
4月前
|
编解码 前端开发 开发者
【Flutter前端技术开发专栏】Flutter中的响应式设计与自适应布局
【4月更文挑战第30天】Flutter框架助力移动应用实现响应式设计与自适应布局,通过层次化布局系统和`Widget`树管理,结合`BoxConstraints`定义尺寸范围,实现自适应。利用`MediaQuery`获取设备信息,调整布局以适应不同屏幕。`FractionallySizedBox`按比例设定尺寸,`LayoutBuilder`动态计算布局。借助这些工具,开发者能创建跨屏幕尺寸、方向兼容的应用,提升用户体验。
115 0
【Flutter前端技术开发专栏】Flutter中的响应式设计与自适应布局
|
4月前
|
开发框架 前端开发 数据安全/隐私保护
【Flutter 前端技术开发专栏】Flutter 中的布局与样式设计
【4月更文挑战第30天】本文探讨了Flutter的布局和样式设计,关键点包括:1) 布局基础如Column、Row和Stack用于创建复杂结构;2) Container、Center和Expanded等常用组件的作用;3) Theme和Decoration实现全局样式和组件装饰;4) 实战应用如登录界面和列表页面的构建;5) 响应式布局利用MediaQuery和弹性组件适应不同屏幕;6) 性能优化,避免过度复杂设计。了解并掌握这些,有助于开发者创建高效美观的Flutter应用。
121 0
【Flutter 前端技术开发专栏】Flutter 中的布局与样式设计
|
2月前
|
容器
flutter 布局管理【详解】
flutter 布局管理【详解】
25 3
|
2月前
flutter 导航组件 AppBar (含顶部选项卡TabBar,抽屉菜单 drawer ,自定义导航图标)
flutter 导航组件 AppBar (含顶部选项卡TabBar,抽屉菜单 drawer ,自定义导航图标)
32 1
|
2月前
Flutter-自定义折叠流布局实现
Flutter-自定义折叠流布局实现
35 0
|
2月前
Flutter 滚动距离来设置TabBar的位置,点击TabBar滚动的到指定的位置
Flutter 滚动距离来设置TabBar的位置,点击TabBar滚动的到指定的位置
|
4月前
|
编解码 算法 开发者
Flutter的布局系统:深入探索布局Widget与布局原则
【4月更文挑战第26天】Flutter布局系统详解,涵盖布局Widget(Row/Column、Stack、GridView/ListView、CustomSingleChildLayout)和布局原则(弹性布局、约束优先、流式布局、简洁明了)。文章旨在帮助开发者理解并运用Flutter的布局系统,创建适应性强、用户体验佳的界面。通过选择合适的布局Widget和遵循原则,可实现复杂且高效的UI设计。
|
4月前
|
容器
Flutter笔记:Box协议的布局约束原理与应用
Flutter笔记:Box协议的布局约束原理与应用
81 0
|
4月前
|
开发者 索引 容器
Flutter开发笔记:Flutter 布局相关组件
Flutter开发笔记:Flutter 布局相关组件
219 0
|
Android开发 UED
Flutter控件之Tab选项卡封装
Tab选项卡,这是一个非常常见且权重很高的一个组件,随便打开一个App,比如掘金,如下图,首页顶部就是一个Tab选项卡,这个功能可以说,几乎每个App都会存在。
208 0