Loading

【Flutter】容器类组件之Scaffold、TabBar、底部导航

前言

一个完整的路由页可能会包含导航栏、抽屉菜单(Drawer)以及底部Tab导航菜单等。Flutter Material组件库提供了一些现成的组件来减少开发任务。Scaffold是一个路由页的骨架,使用它可以很容易地拼装出一个完整的页面。

接口描述

AppBar是一个Material风格的导航栏,通过它可以设置导航栏标题、导航栏菜单、导航栏底部的Tab标题等。

AppBar({
    Key key,
    // 导航栏最左侧的widget,常见为抽屉菜单按钮或返回按钮。可手动来设置这一项
    this.leading,
    // 如果leading为null,是否自动实现默认的leading按钮
    this.automaticallyImplyLeading = true,
    // 页面标题
    this.title,
    // 导航栏右侧菜单
    this.actions,
    this.flexibleSpace,
    // 导航栏底部菜单,通常为Tab按钮组
    this.bottom,
    // 导航栏阴影
    this.elevation,
    this.shape,
    this.backgroundColor,
    this.brightness,
    this.iconTheme,
    this.actionsIconTheme,
    this.textTheme,
    this.primary = true,
    // 标题时候居中
    this.centerTitle,
    this.titleSpacing = NavigationToolbar.kMiddleSpacing,
    this.toolbarOpacity = 1.0,
    this.bottomOpacity = 1.0,
  }) : assert(automaticallyImplyLeading != null),
       assert(elevation == null || elevation >= 0.0),
       assert(primary != null),
       assert(titleSpacing != null),
       assert(toolbarOpacity != null),
       assert(bottomOpacity != null),
       preferredSize = Size.fromHeight(kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),
       super(key: key);

Material组件库中提供了一个TabBar组件,它可以快速生成Tab菜单。

const TabBar({
    Key key,
    @required this.tabs,
    this.controller,
    this.isScrollable = false,
    this.indicatorColor,
    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.onTap,
  }) : 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);

Material库提供了一个TabBarView组件,通过它不仅可以轻松的实现Tab页,而且可以非常容易的配合TabBar来实现同步切换和滑动状态同步.

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

无论是点击导航栏Tab菜单还是在页面上左右滑动,Tab页面都会切换,并且Tab菜单的状态和Tab页面始终保持同步!那它们是如何实现同步的呢?因为TabBar和TabBarView的controller是同一个!正是如此,TabBar和TabBarView正是通过同一个controller来实现菜单切换和滑动状态同步的。

代码示例

// Scaffold、TabBar、底部导航

import 'package:flutter/material.dart';

class ScaffoldTest extends StatefulWidget {
  @override
  _ScaffoldTestState createState() => _ScaffoldTestState();
}

class _ScaffoldTestState extends State<ScaffoldTest> with SingleTickerProviderStateMixin{
  int _selectedIndex = 2;
  TabController _tabController;
  List tabs = ['会话', '系统'];

  @override
  void initState() {
    print('初始化了数据!');
    super.initState();
    // 创建Controller
    _tabController = TabController(length: tabs.length, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // AppBar是一个Material风格的导航栏,通过它可以设置导航栏标题、导航栏菜单、导航栏底部的Tab标题等
      appBar: AppBar(

        // 导航栏最左侧的widget
        leading: Builder(builder: (context) {
          return IconButton(
            // 自定义图标
            icon: Icon(Icons.dashboard, color: Colors.red,),
            onPressed: () {
              // 打开抽屉菜单
              Scaffold.of(context).openDrawer();
            },
          );
        }),

        // 导航栏标题
        title: Text('Test App'),

        // 导航栏右侧菜单
        actions: <Widget>[
          IconButton(icon: Icon(Icons.share),onPressed: () {},),
        ],

        // Tab菜单
        bottom: TabBar(
          controller: this._tabController,
//          isScrollable: true,
          tabs: <Widget>[
            Tab(text: tabs[0]),
            Tab(text: tabs[1]),
          ],
        ),

      ),

      // 抽屉
      drawer: MyDrawer(),

      body: TabBarView(
        controller: this._tabController,
        children: <Widget>[
          Container(
            alignment: Alignment.center,
              child: Text(tabs[0], textScaleFactor: 10),
          ),
          Container(
            alignment: Alignment.center,
            child: Text(tabs[1], textScaleFactor: 10),
          ),
        ],
      ),

      // 底部导航
      bottomNavigationBar: BottomNavigationBar(
        items: <BottomNavigationBarItem>[
          BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('首页'), backgroundColor: Colors.blue,),
          BottomNavigationBarItem(icon: Icon(Icons.system_update_alt), title: Text('数据'), backgroundColor: Colors.blue,),
          BottomNavigationBarItem(icon: Icon(Icons.notifications_active), title: Text('通知'), backgroundColor: Colors.blue,),
          BottomNavigationBarItem(icon: Icon(Icons.settings), title: Text('设置'), backgroundColor: Colors.blue,),
        ],
        currentIndex: _selectedIndex,
        fixedColor: Colors.red,
        onTap: _onItemTapped,
      ),

      // 悬浮按钮
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: _onAdd,
        backgroundColor: Colors.blue,
      ),
      // 设置悬浮按钮的位置为底部导航栏的正中间
//      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,

      // 增加打洞按钮
//      bottomNavigationBar: BottomAppBar(
//        color: Colors.green,
//        // 底部导航栏打一个圆形的洞
//        shape: CircularNotchedRectangle(),
//        child: Row(
//          children: <Widget>[
//            IconButton(icon: Icon(Icons.home),),
//            // 中间位置空出
//            SizedBox(),
//            IconButton(icon: Icon(Icons.business),),
//          ],
//          mainAxisAlignment: MainAxisAlignment.spaceAround,
//        ),
//      ),

    );
  }

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  void _onAdd() {
    print('_onAdd');
  }

}


class MyDrawer extends StatelessWidget {

  MyDrawer({Key key,}) : super(key:key);

  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: MediaQuery.removePadding(
          context: context, 
          // 移除抽屉菜单顶部默认留白
          removeTop: true,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[

              Padding(
                padding: const EdgeInsets.only(top: 38.0),
                child: Row(
                  children: <Widget>[
                    Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 16.0),
                      child: ClipOval(
                        child: Image.asset('assets/images/avatar.png', width: 80,),
                      ),
                    ),
                    Text('parzulpan',style: TextStyle(fontWeight: FontWeight.bold),)
                  ],
                ),
              ),

              Expanded(
                child: ListView(
                  children: <Widget>[
                    ListTile(
                      leading: const Icon(Icons.add),
                      title: const Text('增加账户'),
                    ),
                    ListTile(
                      leading: const Icon(Icons.info),
                      title: const Text('账户管理'),
                    ),

                  ],
                ),
              )
            ],
          )
      ),
    );
  }

}

总结

暂无

posted @ 2019-12-31 10:56  Parzulpan  阅读(924)  评论(0编辑  收藏  举报