Flutter滑动列表隐藏头部ListView+TabBar悬浮的实现

先来张效果图


我的需求是,列表滚动到顶部,Tabbar停留,置顶.
实际写起来,绕了不少弯路.

最开始我使用的:
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
import 'package:flutter/material.dart';


const url =
'http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg';

class TestPage2 extends StatefulWidget {
@override
_TestPage2State createState() => _TestPage2State();
}

class _TestPage2State extends State<TestPage2> {
var tabTitle = [
'页面1',
'页面2',
'页面3',
];

@override
Widget build(BuildContext context) {
return new DefaultTabController(
length: tabTitle.length,
child: Scaffold(
body: new CustomScrollView(
slivers: <Widget>[
new SliverAppBar(
expandedHeight: 200.0,
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(
"我是可以跟着滑动的title",
),
background: Image.network(
url,
fit: BoxFit.cover,
)),
),
new SliverToBoxAdapter(
child: new TabBar(
tabs: tabTitle.map((f) => Tab(text: f)).toList(),
indicatorColor: Colors.red,
unselectedLabelColor: Colors.black,
labelColor: Colors.red,
),
),
new SliverFillRemaining(
child: TabBarView(
children: tabTitle
.map((s) => ListView.builder(
itemBuilder: (context, int) => Text("123"),
itemCount: 50,
))
.toList(),
),
)
],
),
));
}
}

虽然列表效果出来了,但不是我想要的,列表滑动,不会联动,使用SliverToBoxAdapter 并不会有联动效果.因为它是固定的.

想要联动,只有像NestedScrollView源码里的

使用自定义的PrimaryScrollController才行.

不过既然NestedScrollView已经做,不是特别的需求,就用NestedScrollView就好了

正确的姿势 NestedScrollView:

SliverAppBar
相当于Appbar.不过是Sliver滚动家族里的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
new SliverAppBar(
expandedHeight: 200.0,
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(
"我是可以跟着滑动的title",
),
background: Image.network(
url,
fit: BoxFit.cover,
)),
),

悬浮最关键的:SliverPersistentHeader
这个是可以联动并且可以停留在顶部的
设置悬浮停留的属性pinned.
这里需要自己实现一个Delegate.因为需要停留weight的高度.

1
2
3
4
5
6
7
8
9
10
11
12
new SliverPersistentHeader(
delegate: new SliverTabBarDelegate(
new TabBar(
tabs: tabTitle.map((f) => Tab(text: f)).toList(),
indicatorColor: Colors.red,
unselectedLabelColor: Colors.black,
labelColor: Colors.red,
),
color: Colors.white,
),
pinned: true,
),

最终代码:

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
import 'package:flutter/material.dart';

const url =
'http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg';

class TestPage extends StatefulWidget {
@override
_TestPageState createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
var tabTitle = [
'页面1',
'页面2',
'页面3',
];

@override
Widget build(BuildContext context) {
return new DefaultTabController(
length: tabTitle.length,
child: Scaffold(
body: new NestedScrollView(
headerSliverBuilder: (context, bool) {
return [
SliverAppBar(
expandedHeight: 200.0,
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(
"我是可以跟着滑动的title",
),
background: Image.network(
url,
fit: BoxFit.cover,
)),
),
new SliverPersistentHeader(
delegate: new SliverTabBarDelegate(
new TabBar(
tabs: tabTitle.map((f) => Tab(text: f)).toList(),
indicatorColor: Colors.red,
unselectedLabelColor: Colors.black,
labelColor: Colors.red,
),
color: Colors.white,
),
pinned: true,
),
];
},
body: TabBarView(
children: tabTitle
.map((s) => ListView.builder(
itemBuilder: (context, int) => Text("123"),
itemCount: 50,
))
.toList(),
),
),
));
}
}

class SliverTabBarDelegate extends SliverPersistentHeaderDelegate {
final TabBar widget;
final Color color;

const SliverTabBarDelegate(this.widget, {this.color})
: assert(widget != null);

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

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

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

@override
double get minExtent => widget.preferredSize.height;
}

题外话:

如果不使用TabBar+TabBarView
那么直接使用CustomScrollView就可以了.
使用SliverList代替ListView就可以进行联动.

flutter开源项目地址