Richard's Blog

动态浮动按钮

字数统计: 951阅读时长: 4 min
2019/03/06 Share

本文来自于我自己学习Flutter时所学习的教程的中文翻译,原文链接DYNAMIC SLIVER FLOATINGACTIONBUTTON

英语水平有限,内容未必准确

在这篇文章中我将一步步创建一个固定在FlexibleSpaceBar(下面那样)边缘的FloatingActionButton。让我们开始吧。

长求总:我(作者)发布在pub package,这样你也可以实现类似的FAB行为。请随意使用和扩展。🙂

dynamic-sliver-floatingactionbutton1

问题

想实现上面那样固定FloatingActionButton的主要问题是在CustomScrollViewSliverList中没有地方把一个Widget放置在SliverAppBarSlivers之间。为了实现这个效果,我们不得不把FLatingActionButton放在CustomScrollView外面,用一个Stack装起来。下面是我们的起点:

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
class MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(
children: <Widget>[
new CustomScrollView(
slivers: [
new SliverAppBar(
expandedHeight: 256.0,
pinned: true,
flexibleSpace: new FlexibleSpaceBar(
title: new Text("SliverFab Example"),
background: new Image.asset(
"img.jpg",
fit: BoxFit.cover,
),
),
),
new SliverList(
delegate: new SliverChildListDelegate(
new List.generate(
20,
(int index) => new ListTile(title: new Text("Item $index")),
),
),
),
],
),
new Positioned(
top: 256.0,
right: 16.0,
child: new FloatingActionButton(
onPressed: () {},
child: new Icon(Icons.add),
),
),
],
),
);
}
}

上面的代码看起来是这样的:

dynamic-sliver-floatingactionbutton2

固定FloatingActionButton

如你所见,我们有了个漂亮的FloatingActionButton,在用户没有滑动内容的时候会固定在AppBar的边缘。但这不是我们想要的。为了真正把FloatingActionButton固定在AppBar的边缘,我们需要使用ScrollController去关注滑动的距离然后改变fab的位置。首先让我们加一个可以更新组件状态的ScrollController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyHomePageState extends State<MyHomePage> {
ScrollController _scrollController;

@override
void initState() {
super.initState();
_scrollController = new ScrollController();
_scrollController.addListener(() => setState(() {}));
}

@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
...
}

然后加到CustomScrollView中:

1
2
3
4
5
6
new CustomScrollView(
controller: _scrollController,
slivers: [
...
]
)

然后让我们根据滑动的距离改变FloatActionButton的位置。为了实现这个功能我提取了FloatingActionButton的创建过程到新的方法中。一个值得注意的地方是在第一次构建_scrollController时还没有连接到scrollView,所以我们不能直接获取offset的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Widget _buildFab() {
double top = 256.0 - 4.0; //default top margin, -4 for exact alignment
if (_scrollController.hasClients) {
top -= _scrollController.offset;
}
return new Positioned(
top: top,
right: 16.0,
child: new FloatingActionButton(
onPressed: () => {},
child: new Icon(Icons.add),
),
);
}

现在的结果看起来是这样的:

dynamic-sliver-floatingactionbutton3

隐藏FloatingActionButton

如你所见,我们的FloatingActionButton滑出了屏幕。我们来让它在靠近顶端的时候隐藏。为了做这个我们可以使用Opacity组件,但是这个Fab只是消失的方案的视觉效果不怎么好。在我认为更好的方案是FloatingActionButton的尺寸缩小,让我们来试试吧:

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
Widget _buildFab() {
//starting fab position
final double defaultTopMargin = 256.0 - 4.0;
//pixels from top where scaling should start
final double scaleStart = 96.0;
//pixels from top where scaling should end
final double scaleEnd = scaleStart / 2;

double top = defaultTopMargin;
double scale = 1.0;
if (_scrollController.hasClients) {
double offset = _scrollController.offset;
top -= offset;
if (offset < defaultTopMargin - scaleStart) {
//offset small => don't scale down
scale = 1.0;
} else if (offset < defaultTopMargin - scaleEnd) {
//offset between scaleStart and scaleEnd => scale down
scale = (defaultTopMargin - scaleEnd - offset) / scaleEnd;
} else {
//offset passed scaleEnd => hide fab
scale = 0.0;
}
}

return new Positioned(
top: top,
right: 16.0,
child: new Transform(
transform: new Matrix4.identity()..scale(scale),
alignment: Alignment.center,
child: new FloatingActionButton(
onPressed: () => {},
child: new Icon(Icons.add),
),
),
);
}

ScaleStartScaleEnd的值是可以根据你希望的行为进行更改。我找到的这些值是符合我的审美。😀

我们做到了!现在我们有一个很好的FloatingActionButton放在AppBar的边缘,就像下面这样:

dynamic-sliver-floatingactionbutton4

感觉这样的界面可能会有其他人需要,我把它发布到了pub package上。我是根据我的需求设计的这个界面,如果你发现任何Bug或缺少的功能,我非常欢迎任何pull requests🙂

你可以在这里找到这篇文章的代码,现在代码的repo在这里

如果你有任何疑问,欢迎留言!🙂
(゜-゜)つロ 干杯~🙂

CATALOG
  1. 1. 问题
  2. 2. 固定FloatingActionButton
  3. 3. 隐藏FloatingActionButton