Flutter开发者

SliverAppBar

2018-07-16

前言

前一段时间由于个人原因消失了一段时间,哈哈,想我没?抱歉.0.0

好吧,我们还是来开始看下今天的内容吧。在前面的文章中我们将到了Appbar的用户,它类似于Android中的toolbar,但是熟悉Android开发的童鞋应该知道在Android中还有个Collapsing Toolbar的东西,就是一个可以折叠的标题栏效果,不知道长什么样子的童鞋问下周围的小伙伴如何?或者你咨询下你的搜索引擎吧。

接下来我们来介绍下SliverAppBar

SliverAppBar

我看还是先来看下SliverAppBar的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

SliverAppBar({
Key key,
this.leading,//前导标题
this.automaticallyImplyLeading: true,
this.title,//标题
this.actions,//菜单
this.flexibleSpace,//可以展开区域,通常是一个FlexibleSpaceBar
this.bottom,//底部内容区域
this.elevation,//阴影
this.forceElevated: false,
this.backgroundColor,背景颜色
this.brightness,//主题明亮
this.iconTheme,图标主题
this.textTheme,//文字主题
this.primary: true,//是否预留高度
this.centerTitle,标题是否居中
this.titleSpacing: NavigationToolbar.kMiddleSpacing,
this.expandedHeight,//展开高度
this.floating: false,//是否随着滑动隐藏标题
this.pinned: false,//是否固定在顶部
this.snap: false,//与floating结合使用
})

构造方法也是非常的简单,但是我们却不能直接使用它,由官方文档可以看到我们通常结合ScrollView来使用它。

我们结合CustomScrollView来看下例子吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

import 'package:flutter/material.dart';

void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}

class MyApp extends StatelessWidget {
final List<ListItem> listData = [];

@override
Widget build(BuildContext context) {
for (int i = 0; i < 20; i++) {
listData.add(new ListItem("我是测试标题$i", Icons.cake));
}
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 200.0,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text("我是一个帅气的标题",
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
background: Image.network(
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531798262708&di=53d278a8427f482c5b836fa0e057f4ea&imgtype=0&src=http%3A%2F%2Fh.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F342ac65c103853434cc02dda9f13b07eca80883a.jpg",
fit: BoxFit.fill,
)),
),
];
},
body: Center(
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new ListItemWidget(listData[index]);
},
itemCount: listData.length,
),
),
),
);
}
}

class ListItem {
final String title;
final IconData iconData;

ListItem(this.title, this.iconData);
}

class ListItemWidget extends StatelessWidget {
final ListItem listItem;

ListItemWidget(this.listItem);

@override
Widget build(BuildContext context) {
return new InkWell(
child: new ListTile(
leading: new Icon(listItem.iconData),
title: new Text(listItem.title),
),
onTap: () {},
);
}
}

首先我们使用了NestedScrollView中的headerSliverBuilder属性添加了SliverAppBar

然后我们设置展开的高度为200,不让标题栏随着滑动滚动出可视区域

我们使用flexibleSpace来构建了一个可以滚动的区域

最后我们给NestedScrollView的body加了一个ListView

然后我们来看下效果:

我们把 pinned的属性设置为false再看下效果

接下来我们来看下bottom属性,允许我们在在下面放置你想放置其他Widget,好吧我们来放个TabBar看下

其实代码很简单,只不过我们需要让MyApp继承于 StatefulWidget,让后让State TickerProviderStateMixin

1
2
3
4
5
6
7
8
9
10
11
class MyApp extends StatefulWidget{
@override
State<StatefulWidget> createState() {

return new MyAppState();
}
}
class MyAppState extends State<MyApp>with TickerProviderStateMixin {
……
……
}

让后给SliverAppBar增加如下代码即可。

1
2
3
4
5
6
7
8
9
10
11
12

bottom: TabBar(
controller: new TabController(length: 2, vsync: this),
labelColor: Colors.lightBlue,
indicatorColor: Colors.lightBlue,
unselectedLabelColor: Colors.grey,
tabs: [
new Tab(icon: new Icon(Icons.info), text: "Info"),
new Tab(
icon: new Icon(Icons.cake), text: "cake"),
],
),

当然我们是希望这个TabBar在SliverAppBar下方,并且随着SliverAppBar滚动的,但是我们还是来看下效果吧

很丑有没有,由于TabBar的高度所以我们并不能让SliverAppBar滑动到顶部,所以要想实现随着SliverAppBar的移动,把TabBar放在bottom也不是很合适的。
在这里,我们可以借助于SliverPersistentHeader中的SliverPersistentHeader属性来解决

SliverPersistentHeader的构造很简单,只有简单的几个属性,不再具体讲了

全部代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

import 'package:flutter/material.dart';

void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}

class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new MyAppState();
}
}

class MyAppState extends State<MyApp> with TickerProviderStateMixin {
final List<ListItem> listData = [];

@override
Widget build(BuildContext context) {
for (int i = 0; i < 20; i++) {
listData.add(new ListItem("我是测试标题$i", Icons.cake));
}
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 200.0,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text("我是一个帅气的标题",
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
background: Image.network(
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531798262708&di=53d278a8427f482c5b836fa0e057f4ea&imgtype=0&src=http%3A%2F%2Fh.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F342ac65c103853434cc02dda9f13b07eca80883a.jpg",
fit: BoxFit.fill,
)),
),

SliverPersistentHeader(
delegate: _SliverAppBarDelegate(
TabBar(

controller: new TabController(length: 2, vsync: this),
labelColor: Colors.black87,
unselectedLabelColor: Colors.grey,
tabs: [
Tab(icon: Icon(Icons.security), text: "security"),
Tab(icon: Icon(Icons.cake), text: "cake"),
],
),
))
];
},
body: Center(
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new ListItemWidget(listData[index]);
},
itemCount: listData.length,
),
),
),
);
}
}

class ListItem {
final String title;
final IconData iconData;

ListItem(this.title, this.iconData);
}

class ListItemWidget extends StatelessWidget {
final ListItem listItem;

ListItemWidget(this.listItem);

@override
Widget build(BuildContext context) {
return new InkWell(
child: new ListTile(
leading: new Icon(listItem.iconData),
title: new Text(listItem.title),
),
onTap: () {},
);
}
}

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate(this._tabBar);

final TabBar _tabBar;

@override
double get minExtent => _tabBar.preferredSize.height;

@override
double get maxExtent => _tabBar.preferredSize.height;

@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return new Container(
child: _tabBar,
);
}

@override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return false;
}
}

效果如下:

但是有童鞋就问问了,我们怎么让这个SliverPersistentHeader中的内容(TabBar)不随着ListView的滚动而滑动呢?其实很简单,因为SliverPersistentHeader跟SliverAppBar一样都有一个 pinned属性,将它设置为true这里面的内容就会在到达顶部后停止跟随ListView移动了。

本期小结

  • 熟悉SliverAppBar的用法
  • 了解CustomScrollView的用法
  • 了解SliverPersistentHeader的用法
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

打赏请备注姓名或者昵称,方便我后期统计哦

关注公众号,及时查阅最新文章