【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('账户管理'),
),
],
),
)
],
)
),
);
}
}
总结
暂无