零、前言
如果你进入过FlutterUnit,那么主页中头部的Tap栏你应该印象深刻。 如下效果: 在上滑时Tap栏会
逐渐变矮
,直到最小值。下拉到顶时变矮的Tap栏会逐渐变高
,直到最大值 FlutterUnit本身主页比较复杂,本文就来写一个最简实践,用最少的代码来实现这个效果。 本文的主人公是SliverPersistentHeader
,来一起看一下它的用法。
上滑效果 | 下拉效果 |
---|---|
一、项目初始
1. 程序入口
代码语言:javascript复制在 main 函数中使用
SystemChrome.setSystemUIOverlayStyle
让状态栏变透明 测试 demo 的核心组件在SliverPersistentHeaderDemo
void main() {
//设置透明 状态栏
SystemUiOverlayStyle style = SystemUiOverlayStyle(statusBarColor: Colors.transparent);
SystemChrome.setSystemUIOverlayStyle(style);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData( primarySwatch: Colors.blue ),
home: Scaffold(
body: SliverPersistentHeaderDemo(),
));
}
}
2.构建色彩列表
我们要构建的是比较复杂的滑动效果, 可以使用
CustomScrollView
,其中slivers接收Sliver家族组件的列表。CustomScrollView
不是本文的主人公,这里不多说,以后会有专篇。 如下:_buildSliverList
负责构建SliverList,其中颜色的item组件的构建交由_buildColorItem
。我建议大家可以把构建的粒度细分一下,不要什么都塞一块,这样看起来会比较清晰。
色彩列表 | 色彩列表 |
---|---|
class SliverPersistentHeaderDemo extends StatelessWidget {
// 色彩数据
final List<Color> data = List.generate(24, (i) => Color(0xFFFF00FF - 24*i));
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
// TODO 添加 bar
_buildSliverList()
],
);
}
// 构建颜色列表
Widget _buildSliverList() =>
SliverList(
delegate: SliverChildBuilderDelegate(
(_, int index) => _buildColorItem(data[index]),
childCount: data.length),
);
// 构建颜色列表item
Widget _buildColorItem(Color color) =>
Card(
child: Container(
alignment: Alignment.center,
width: 100,
height: 60,
color: color,
child: Text(
colorString(color),
style: const TextStyle(
color: Colors.white,
shadows: [
Shadow(color: Colors.black,
offset: Offset(.5, .5),
blurRadius: 2)
]),
),
),
);
// 颜色转换为文字
String colorString(Color color) =>
"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}";
}
这样初始的demo就搭建好了,下面来看看
SliverPersistentHeader
的用法吧
二、认识 SliverPersistentHeader
1.SliverPersistentHeader
属性一览
属性名 | 类型 | 默认值 | 介绍 |
---|---|---|---|
delegate | SliverPersistentHeaderDelegate | required | 组件构建代理 |
pinned | bool | false | 是否固定 |
floating | bool | false | 是否浮动 |
2.SliverPersistentHeaderDelegate
的使用
估计很多人看到
XXXDelegate
就有种劝退的感觉。先别怕,看看它是什么。 可以看到它是抽象类,说明需要实现一些抽象方法,而一般抽象方法都会为我们回调一些有价值的东西 查看他的族谱,发现没有可以使用的子类,那么想使用它,二话不说,先写个他的子类。
代码语言:javascript复制现在写一个
UnitPersistentHeaderDelegate
实现一下SliverPersistentHeaderDelegate, 可以看到有下面四个需要实现的方法:build
,maxExtent
,minExtent
,shouldRebuild
class UnitPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
print(
"=====shrinkOffset:$shrinkOffset======overlapsContent:$overlapsContent====");
final String info =
'shrinkOffset:${shrinkOffset.toStringAsFixed(1)}'
'noverlapsContent:$overlapsContent';
return Container(
alignment: Alignment.center,
color: Colors.orangeAccent,
child: Text(
info,
style: TextStyle(fontSize: 20, color: Colors.white),
),
);
}
@override
double get maxExtent => 120;
@override
double get minExtent => 80;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
false;
}
先简单实现一下使用上面定义的
UnitPersistentHeaderDelegate
查看效果: 可以看到上面的build方法的作用就是构建组件,shrinkOffset
为偏移量 头部栏组件开始完全展开maxExtent
高度,随着列表上滑而上滑,可以从日志里看出最大上滑高度为maxExtent
,这是默认pinned=false
,floating=false
的滑动效果。
上滑测试 | 下拉测试 |
---|---|
//英雄所见...
return CustomScrollView(
slivers: <Widget>[
_buildPersistentHeader(), //<-- 在列表上方创建PersistentHeader
_buildSliverList()
],
);
}
// 使用UnitPersistentHeaderDelegate创建PersistentHeader
Widget _buildPersistentHeader() => SliverPersistentHeader(
delegate: UnitPersistentHeaderDelegate());
3.SliverPersistentHeader的pinned与floating
属性
下面开始试验:
pinned_true_floating_false | pinned_true_floating_true |
---|---|
上滑:顶部会留出minExtent的高度,不再随上滑而减小 | 上滑:顶部会留出minExtent的高度,不再随上滑而减小 |
下拉:直到滑到顶端时,剩余空间才会展开 | 下拉: 任意位置下拉时, 剩余空间会展开 |
下面开始试验:
pinned_false_floating_false | pinned_false_floating_true |
---|---|
上滑: 顶部会滑出 | 上滑:顶部会滑出 |
下拉:直到滑到顶端时,顶部才会展开 | 下拉: 任意位置下拉时, 空间会展开 |
三、使用 SliverPersistentHeader
1. 封装PersistentHeaderBuilder
代码语言:javascript复制上面使用起来比较麻烦,可以自定义一个PersistentHeaderBuilder来简化构建 使用builder属性,将创建的逻辑移交到使用时,可以回调一些有价值的数据,比如偏移量
class PersistentHeaderBuilder extends SliverPersistentHeaderDelegate {
final double max;
final double min;
final Widget Function(BuildContext context, double offset) builder;
PersistentHeaderBuilder(
{this.max = 120, this.min = 80, @required this.builder})
: assert(max >= min && builder != null);
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return builder(context, shrinkOffset);
}
@override
double get maxExtent => max;
@override
double get minExtent => min;
@override
bool shouldRebuild(covariant PersistentHeaderBuilder oldDelegate) =>
max != oldDelegate.max ||
min != oldDelegate.min ||
builder != oldDelegate.builder;
}
2.使用PersistentHeaderBuilder
代码语言:javascript复制 Widget _buildPersistentHeader() => SliverPersistentHeader(
pinned: true,
floating: false,
delegate: PersistentHeaderBuilder(builder: (ctx, offset) => Container(
alignment: Alignment.center,
color: Colors.orangeAccent,
child: Text(
"shrinkOffset:${offset.toStringAsFixed(1)}",
style: TextStyle(fontSize: 20, color: Colors.white),
),
)));
3.多个SliverPersistentHeader的使用
你也可以根据offset来进行一些变换处理。 多个SliverPersistentHeader是可以共存的,如下
上滑 | 下拉 |
---|---|
Widget _buildPersistentHeader2() => SliverPersistentHeader(
pinned: false,
floating: false,
delegate: PersistentHeaderBuilder(
max: 100,
builder: (ctx, offset) => Container(
transform: Matrix4.rotationZ(offset / 120 * pi / 2),
alignment: Alignment.center,
color: Colors.blue,
child: Text(
"shrinkOffset:${offset.toStringAsFixed(1)}",
style: TextStyle(fontSize: 20, color: Colors.white),
),
)));
SliverPersistentHeader
基本用法就是这样,你可以基于此实现很多有意思的滑动效果 最后欢迎关注我的开源项目 FlutterUnit,FlutterUnit相关的周边文章会陆续更新,其中包括一些Flutter组件的用法,或一些FlutterUnit实现的细节,FlutterUnit的重大更新等,欢迎持续关注。
@张风捷特烈 2020.10.25 未允禁转
~ END ~