在本栏的前面章节的学习中,咱们基本上把Flutter中全部的经常使用布局、组件包括多页面跳转路由都介绍过了,细心的读者可能会发如今前面的课程中咱们每次新建一个
Flutter Page
的时候都会在根布局的build
方法中直接return一个Scaffold
而后,再经过配置Scaffold中的相关属性来快速的渲染页面布局:没错Scaffold的出现就是为了帮助咱们实现基本的 Material Design 布局结构,简化咱们页面开发工做,咱们能够形象的把Scaffold
理解为页面搭建的脚手架
java
appBar
: 显示在界面顶部的一个菜单导航栏body
:页面中显示主要内容的区域,可任意指定WidgetfloatingActionButton
: 悬浮按钮(相似原生Android中的floatingActionButton)drawer、endDrawer
:分别用于在左右两侧边栏展现抽屉菜单bottomNavigationBar
:显示在底部的导航栏按钮栏在Flutter脚手架中给咱们提供了上述用于快速构建页面的经常使用属性,开发者可根据本身的页面需求,选择性的引入不一样属性达到定制出不一样UI呈现的目的,关于Scaffold中的其余属性,我就不逐个讲解了,下面我结合代码跟你们一块测试下上述经常使用方法。git
在前面的每一节课程中咱们均可以找到appBar的身影,可是因为以前的课程重点不在appBar上,因此咱们并无对appBar展开过多的介绍,就仅仅是做为展现页面标题使用。github
咱们先来看看Scaffold源码中对appBar的解释说明:一个展现在Scaffold顶部的APP Bar数组
/// An app bar to display at the top of the scaffold.
final PreferredSizeWidget appBar;
复制代码
一般咱们会给根据不一样的业务场景设置不一样的AppBar,关于AppBar的构造方法大部分属性读者均可以根据属性名自解释,我就不贴出来赘述了,下面我举几个经常使用的例子app
appBar: AppBar(
title: Text("Scaffold 脚手架"),
),
复制代码
标题居中:less
appBar: AppBar(
title: Text("Scaffold 脚手架"),
centerTitle: true,
),
复制代码
从上面的代码以及图片中咱们看到Scaffold中默认为咱们在appBar上指定了一个返回的箭头,点击箭头返回上一页,固然咱们能够经过leading
属性自定义左上角的图标,可是当咱们指定了leading
点击事件就须要咱们本身单独处理了,也就是说,指定了leading
以后,咱们就没办法在点击箭头的时候结束当前页,可是咱们能够本身实现这一操做。ide
appBar: AppBar(
leading: GestureDetector(child: Icon(Icons.print),onTap: (){
Navigator.of(context).pop();
}), //添加leading以后须要重写点击事件唤
title: Text("Scaffold 脚手架"),
centerTitle: true,
),
复制代码
在Flutter中咱们经过Appbar的 actions属性设置菜单项,通常重要的菜单选项咱们会直接放在右边bar上显示,非重要功能选项咱们会经过PopupMenuButton以三个小点的形式放进折叠菜单里,下面咱们结合源码看下效果图,读者一看便知。布局
appBar: AppBar(
leading: GestureDetector(child: Icon(Icons.print),onTap: (){
Navigator.of(context).pop();
}), //添加leading以后须要重写点击事件唤起抽屉菜单
title: Text("Scaffold 脚手架"),
actions: <Widget>[
IconButton(icon: Icon(Icons.message), onPressed: () {}),
IconButton(icon: Icon(Icons.access_alarm), onPressed: () {}),
PopupMenuButton(
onSelected: (String value) {
print('-----------------$value');
},
itemBuilder: (BuildContext context) => [
new PopupMenuItem(value: "选项一的内容", child: new Text("选项一")),
new PopupMenuItem(value: "选项二的内容", child: new Text("选项二")),
new PopupMenuItem(value: "选项三的内容", child: new Text("选项三")),
])
],
),
复制代码
在原生Android中咱们很熟悉的就是利用TabLayout设置标题栏下方的tab切换效果,在Flutter中咱们经过给AppBar的bottom属性设置TabBar来完成这一效果。学习
bottom: TabBar(
controller: _tabController,
tabs: topTabLists
.map((element) => Tab(
text: element,
))
.toList(),
// onTap: (index) => {},
)),
复制代码
使用TabBar必须传入controller属性,咱们经过initState()方法初始化_tabController测试
@override
void initState() {
super.initState();
//初始化顶部TabController
_tabController = TabController(length: topTabLists.length, vsync: this);
}
复制代码
bottom: TabBar(
controller: _tabController,
tabs: topTabLists
.map((element) => Tab(
text: element,
icon: Icon(Icons.print),
))
.toList(),
// onTap: (index) => {},
)),
复制代码
根据Scaffold页面布局的上下顺序,下面咱们讲解第二部分body
部分。关于body部分在前面的课程中,咱们使用了不少次了,也没什么须要特别说的地方,这里我就不展开介绍了,body做为一个页面的主要视图部分,能够传入任意指定的Widget,可现实在屏幕中央,这里我就不过多赘述了。
body: Container(
child: Text("内容区域"),
),
复制代码
在文章开题的时候,我提到了抽屉菜单能够经过drawer、endDrawer指定左右两侧打开的抽屉菜单。下面咱们就来看下效果。
左侧抽屉
drawer: MyDrawer(), //MyDrawer详细代码在下面
复制代码
右侧抽屉
endDrawer: MyDrawer(), //MyDrawer详细代码在下面
复制代码
MyDrawer
代码以下
class MyDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 88.0, bottom: 30.0),
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ClipOval(
child: Image.network(
"https://avatar.csdn.net/6/0/6/3_xieluoxixi.jpg",
width: 60,
),
),
),
Text(
"谢栋",
style: TextStyle(fontWeight: FontWeight.bold),
)
],
),
),
Expanded(
child: ListView(
children: <Widget>[
ListTile(
leading: const Icon(Icons.settings), title: const Text('我的设置'), ), ListTile( leading: const Icon(Icons.live_help), title: const Text('帮助说明'), ), ListTile( leading: const Icon(Icons.settings), title: const Text('我的设置'), ), ListTile( leading: const Icon(Icons.live_help), title: const Text('帮助说明'), ), ], ), ) ], ));
}
}
复制代码
floatingActionButton在前面讲解Button章节中咱们讲解过,知识点也比较简单,就是在页面布局快速构建出一个悬浮按钮。
floatingActionButton: FloatingActionButton(
onPressed: (){
print("---------");
},
child: Icon(Icons.add),
),
复制代码
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked, //设置FloatingActionButton的位置
);
复制代码
bottomNavigationBar的使用场景还比较多,通常咱们的多页面app都会经过底部的Tab来切换App首页展现的不一样内容,在Flutter的Scaffold中为咱们提供了快捷用于构建底部Tab的方法,咱们经过给BottomNavigationBar的Items
属性设置须要展现的BottomNavigationBarItem数组便可。
bottomNavigationBar: BottomNavigationBar(
//不设置该属性多于三个不显示颜色
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("首页")),
BottomNavigationBarItem(icon: Icon(Icons.message), title: Text("消息")),
BottomNavigationBarItem(
icon: Icon(Icons.add_a_photo), title: Text("动态")),
BottomNavigationBarItem(icon: Icon(Icons.person), title: Text("个人"))
],
currentIndex: _currentBottomIndex,
fixedColor: Colors.blue,
onTap: (index) => _onBottomTabChange(index),
),
复制代码
多于三个就变成白色了,不能正常显示
。
这里我从源码角度上给读者解读一下。
/// If [fixedColor] is null then the theme's primary color,
/// [ThemeData.primaryColor], is used. However if [BottomNavigationBar.type] is
/// [BottomNavigationBarType.shifting] then [fixedColor] is ignored.
BottomNavigationBar({
Key key,
@required this.items,
this.onTap,
this.currentIndex = 0,
BottomNavigationBarType type,
this.fixedColor,
this.iconSize = 24.0,
}) : assert(items != null),
assert(items.length >= 2),
assert(
items.every((BottomNavigationBarItem item) => item.title != null) == true,
'Every item must have a non-null title',
),
assert(0 <= currentIndex && currentIndex < items.length),
assert(iconSize != null),
type = type ?? (items.length <= 3 ? BottomNavigationBarType.fixed : BottomNavigationBarType.shifting),
super(key: key);
复制代码
BottomNavigationBar中的items数量小于等于3时,type为BottomNavigationBarType.fixed,大于3则为BottomNavigationBarType.shifting
,因此咱们只需在代码中重载type属性,大于3个的时候设置type值为
BottomNavigationBarType.fixed
便可。我在一开始的代码注释中也解释了这个问题。
UI小特效 在实现底部导航栏时,Flutter还为咱们提供了一个Material组件中的相似‘"镶嵌"效果,使用BottomAppBar配合FloatingActionButton完成,文字描述可能云里雾里的。
一图胜千言
//与FloatingActionButton配合实现"打洞"效果
bottomNavigationBar: BottomAppBar(
color: Colors.white,
shape: CircularNotchedRectangle(), // 底部导航栏打一个圆形的洞
child: Row(
children: [
Tab(text: "首页", icon: Icon(Icons.home)),
Tab(text: "消息", icon: Icon(Icons.message)),
Tab(text: "动态", icon: Icon(Icons.add_a_photo)),
Tab(text: "个人", icon: Icon(Icons.person)),
],
mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部导航栏横向空间
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _onFabClick,
child: Icon(Icons.add),
),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked, //设置FloatingActionButton的位置
);
复制代码
虽然咱们在上面借助BottomNavigationBar
也实现了相似的效果,可是前者是直接压在导航栏上面的,然后者是嵌入进去的,效果更逼真。读者可对比下方的效果图。