【Flutter】顶部导航栏实现 ( Scaffold | DefaultTabController | TabBar | Tab | TabBarView )

文章目录





一、Scaffold 组件



Flutter 中的 Scaffold 组件实现了基础的材料设计 ( Material Design ) 可视化布局结构 ;

Scaffold 提供了显示左侧侧拉导航栏 , 底部导航 , 浮动按钮等 API ;

Scaffold 构造函数如下 :

class Scaffold extends StatefulWidget {
  /// Creates a visual scaffold for material design widgets.
  const Scaffold({
    Key? key,
    this.appBar,	// 顶部的标题栏
    this.body,		// 中间显示的核心部分 , 标题栏下面的部分都是  
    this.floatingActionButton,			// 右下角的悬浮按钮 ( 可改变位置 )
    this.floatingActionButtonLocation,
    this.floatingActionButtonAnimator,
    this.persistentFooterButtons,
    this.drawer,	// 侧拉导航栏 
    this.onDrawerChanged,
    this.endDrawer,
    this.onEndDrawerChanged,
    this.bottomNavigationBar,
    this.bottomSheet,
    this.backgroundColor,
    this.resizeToAvoidBottomInset,
    this.primary = true,
    this.drawerDragStartBehavior = DragStartBehavior.start,
    this.extendBody = false,
    this.extendBodyBehindAppBar = false,
    this.drawerScrimColor,
    this.drawerEdgeDragWidth,
    this.drawerEnableOpenDragGesture = true,
    this.endDrawerEnableOpenDragGesture = true,
    this.restorationId,
  }) : assert(primary != null),
       assert(extendBody != null),
       assert(extendBodyBehindAppBar != null),
       assert(drawerDragStartBehavior != null),
       super(key: key);




二、实现顶部导航栏



实现顶部导航栏需要三个组件 :

  • TabBar : 该组件就是导航栏组件 , 设置多个图标按钮 ;
  • TabBarView : 该组件是被导航的组件 , 设置多个布局结构 , 同时只能显示一个 ;
  • DefaultTabController : 该组件用于关联控制 TabBar 和 TabBarView 组件 ;

界面组件中 , 根组件肯定是 MaterialApp , 然后下一层组件就是 DefaultTabController , 使用 DefaultTabController 包裹 Scaffold , 然后在 Scaffold 中定义的 TabBarTabBarView 就会被关联再一起 ;


注意三个相等的值 :

DefaultTabController length 长度

等于

TabBar 子组件个数

等于

TabBarView 子组件个数


Google 官方给出的文档 :

[TabBar], which displays a row of tabs. ( 显示一行标签 )
[TabBarView], which displays a widget for the currently selected tab. ( 显示当前选中的标签对应的组件 )
[TabController], which coordinates tab selection between a [TabBar] and a [TabBarView]. ( 用于关联标签与选项卡 )
https://material.io/design/components/tabs.html





三、DefaultTabController 导航标签控制组件



DefaultTabController 用于关联 TabBar 和 TabBarView 组件 ;

由于 TabBar 中的组件都是无状态组件 , 或者不同的父类组件 , 导致创建 TabController 不方便时 , 就会使用该 DefaultTabController 组件 ;

DefaultTabController 组件的 length 参数必须不为空 , 并且大于 1 , length 的个数必须等于 TabBar 和 TabBarView 的个数 ;

initialIndex 初始索引值参数必须不能为空


DefaultTabController 构造函数原型 :

  /// 为给定的子组件创建一个默认的导航控制器
  ///
  /// length 参数必须不为空 , 并且大于 1 ;
  /// length 的个数必须等于 TabBar 和 TabBarView 的个数 ;
  /// 
  /// initialIndex 初始索引值参数必须不能为空 
  const DefaultTabController({
    Key? key,
    required this.length,
    this.initialIndex = 0,
    required this.child,
  }) : assert(initialIndex != null),
       assert(length >= 0),
       assert(length == 0 || (initialIndex >= 0 && initialIndex < length)),
       super(key: key);

Google 官方给出的代码示例 :

class MyDemo extends StatelessWidget {
  final List<Tab> myTabs = <Tab>[
    Tab(text: 'LEFT'),
    Tab(text: 'RIGHT'),
  ];
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: myTabs.length,
      child: Scaffold(
        appBar: AppBar(
          bottom: TabBar(
            tabs: myTabs,
          ),
        ),
        body: TabBarView(
          children: myTabs.map((Tab tab) {
            final String label = tab.text.toLowerCase();
            return Center(
              child: Text(
                'This is the $label tab',
                style: const TextStyle(fontSize: 36),
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}




四、TabBar 导航按钮组件



TabBar 组件主要用于封装导航栏的图标按钮 , 主要设置一组 Tab 组件 ;

通常放在 AppBar 组件的底部 , 也就是赋值给 AppBar.bottom , 与 TabBarView 结合起来使用 ;

TabBar 中 Tab 子组件的个数 , TabController 中的 length 长度 , TabBarView 中子组件的个数 , 三者必须相等 ;


TabBar 构造函数 :

  const TabBar({
    Key? key,
    required this.tabs,
    this.controller,
    this.isScrollable = false,
    this.indicatorColor,
    this.automaticIndicatorColorAdjustment = true,
    this.indicatorWeight = 2.0,
    this.indicatorPadding = EdgeInsets.zero,
    this.indicator,
    this.indicatorSize,
    this.labelColor,
    this.labelStyle,
    this.labelPadding,
    this.unselectedLabelColor,
    this.unselectedLabelStyle,
    this.dragStartBehavior = DragStartBehavior.start,
    this.overlayColor,
    this.mouseCursor,
    this.enableFeedback,
    this.onTap,
    this.physics,
  }) : assert(tabs != null),
       assert(isScrollable != null),
       assert(dragStartBehavior != null),
       assert(indicator != null || (indicatorWeight != null && indicatorWeight > 0.0)),
       assert(indicator != null || (indicatorPadding != null)),
       super(key: key);

官方提供的 TabBar 代码示例 :

late TabController _tabController;
 @override
 void initState() {
   super.initState();
   _tabController = TabController(length: 3, vsync: this);
 }
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text('TabBar Widget'),
       bottom: TabBar(
         controller: _tabController,
         tabs: <Widget>[
           Tab(
             icon: Icon(Icons.cloud_outlined),
           ),
           Tab(
            icon: Icon(Icons.beach_access_sharp),
           ),
           Tab(
             icon: Icon(Icons.brightness_5_sharp),
           ),
         ],
       ),
     ),
     body: TabBarView(
       controller: _tabController,
       children: <Widget>[
         Center(
           child: Text('It\'s cloudy here'),
         ),
         Center(
           child: Text('It\'s rainy here'),
         ),
         Center(
            child: Text('It\'s sunny here'),
         ),
       ],
     ),
   );
 }




五、Tab 标签组件



Tab 组件是 TabBar 组件的子组件 , 每个 TabBar 组件需要设置若干个 Tab 组件 ( 至少一个 ) ;


Tab 构造函数 :

  /// 创建一个材料设计风格的选项卡.
  ///
  /// 至少设置一个 text 文本和 icon 图标 child 必须为非空 .
  const Tab({
    Key? key,
    this.text,
    this.icon,
    this.iconMargin = const EdgeInsets.only(bottom: 10.0),
    this.child,
  }) : assert(text != null || child != null || icon != null),
       assert(text == null || child == null),
       super(key: key);

代码示例 :

bottom: TabBar(
  /// 可左右滑动
  isScrollable: true,
  /// 设置顶部导航栏的图标
  tabs: datas.map((TabData data) {
    /// 导航栏的图标及文本
    return Tab(
      text: data.title,
      icon: Icon(data.icon),
    );
  }).toList(),
),




六、TabBarView 导航主体内容组件



显示 TabBar 中当前选中的 Tab 标签对应的组件 ;

TabBarView 初始化时 , 可以只为其设置 children 参数 , 类型是 List<Widget> ;

TabBarView 构造函数 :

  const TabBarView({
    Key? key,
    required this.children,
    this.controller,
    this.physics,
    this.dragStartBehavior = DragStartBehavior.start,
  }) : assert(children != null),
       assert(dragStartBehavior != null),
       super(key: key);




七、完整代码示例



import 'package:flutter/material.dart';

void main() {
  runApp(
      TabBarWidget()
  );
}

/// 导航栏数据集合
const List<TabData> datas = const <TabData>[
  const TabData(title: '3D', icon: Icons.threed_rotation),
  const TabData(title: '打印机', icon: Icons.print),
  const TabData(title: '动画', icon: Icons.animation),
  const TabData(title: '变换', icon: Icons.transform),
  const TabData(title: '高度', icon: Icons.height),
  const TabData(title: '描述', icon: Icons.description),
  const TabData(title: '向前', icon: Icons.forward),
  const TabData(title: '相机', icon: Icons.camera),
  const TabData(title: '设置', icon: Icons.settings),
  const TabData(title: '学位', icon: Icons.school),
];

/// 顶部导航栏核心页面
class TabBarWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    /// 材料设计应用组件 , 一般作为页面的根组件
    return MaterialApp(

      /// 用于将 TabBar 和 TabBarView 封装起来
      home: DefaultTabController(
        length: datas.length,

        /// 主界面框架
        child: Scaffold(

          /// 标题栏
          appBar: AppBar(

            /// 标题栏标题
            title: const Text('顶部导航栏'),

            /// 设置顶部导航栏
            bottom: TabBar(
              /// 可左右滑动
              isScrollable: true,

              /// 设置顶部导航栏的图标
              tabs: datas.map((TabData data) {

                /// 导航栏的图标及文本
                return Tab(
                  text: data.title,
                  icon: Icon(data.icon),
                );

              }).toList(),
            ),
          ),

          /// 导航栏控制的左右轮播的组件
          body: TabBarView(

            /// 界面显示的主体 , 通过 TabBar 切换不同的本组件显示
            children: datas.map((TabData choice) {
              return Padding(
                padding: const EdgeInsets.all(10.0),
                child: TabContent(data: choice),
              );
            }).toList(),
          ),
        ),
      ),
    );
  }
}

/// 通过 TabBar 导航栏切换展示的主要内容
/// 用于在 TabBarView 中显示的组件
class TabContent extends StatelessWidget {
  const TabContent({Key key, this.data}) : super(key: key);

  /// 根据该数据条目生成组件
  final TabData data;

  @override
  Widget build(BuildContext context) {
    TextStyle textStyle = TextStyle(color: Colors.yellow, fontSize: 50);
    return Card(

      /// 设置 20 像素边距
      margin: EdgeInsets.all(20),

      /// 设置阴影
      elevation: 10,

      /// 卡片颜色黑色
      color: Colors.black,

      /// 卡片中的元素居中显示
      child: Center(

        /// 垂直方向的线性布局
        child: Column(

          /// 在主轴 ( 垂直方向 ) 占据的大小
          mainAxisSize: MainAxisSize.min,

          /// 居中显示
          crossAxisAlignment: CrossAxisAlignment.center,

          children: <Widget>[

            /// 设置图标
            Icon(data.icon, size: 128.0, color: Colors.green),

            /// 设置文字
            Text(data.title, style: TextStyle(color: Colors.yellow, fontSize: 50)),

          ],
        ),
      ),
    );
  }
}

/// 封装导航栏的图标与文本数据
class TabData {
  /// 导航数据构造函数
  const TabData({this.title, this.icon});

  /// 导航标题
  final String title;

  // 导航图标
  final IconData icon;
}

运行效果 :

【Flutter】顶部导航栏实现 ( Scaffold | DefaultTabController | TabBar | Tab | TabBarView )





八、相关资源



参考资料 :


重要的专题 :


博客源码下载 :

上一篇:运行flex布局对单行多个item进行布局的技巧


下一篇:解决QIcon引用qrc不显示图片