零、前言
如果你进入过FlutterUnit,那么主页中头部的Tap栏你应该印象深刻。 如下效果: 在上滑时Tap栏会
逐渐变矮
,直到最小值。下拉到顶时变矮的Tap栏会逐渐变高
,直到最大值 FlutterUnit本身主页比较复杂,本文就来写一个最简实践,用最少的代码来实现这个效果。 本文的主人公是SliverPersistentHeader
,来一起看一下它的用法。
| 上滑效果 | 下拉效果 | | ---- | ---- | | |
|
一、项目初始
1. 程序入口
在 main 函数中使用
SystemChrome.setSystemUIOverlayStyle
让状态栏变透明 测试 demo 的核心组件在SliverPersistentHeaderDemo
```Dart 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
。我建议大家可以把构建的粒度细分一下,不要什么都塞一块,这样看起来会比较清晰。
| 色彩列表 | 色彩列表 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | |
|
```dart class SliverPersistentHeaderDemo extends StatelessWidget { // 色彩数据 final List data = List.generate(24, (i) => Color(0xFFFF00FF - 24*i));
@override Widget build(BuildContext context) { return CustomScrollView( slivers: [ // 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
就有种劝退的感觉。先别怕,看看它是什么。 可以看到它是抽象类,说明需要实现一些抽象方法,而一般抽象方法都会为我们回调一些有价值的东西 查看他的族谱,发现没有可以使用的子类,那么想使用它,二话不说,先写个他的子类。
现在写一个
UnitPersistentHeaderDelegate
实现一下SliverPersistentHeaderDelegate, 可以看到有下面四个需要实现的方法:build
,maxExtent
,minExtent
,shouldRebuild
```dart 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
的滑动效果。
| 上滑测试 | 下拉测试 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | |
|
```dart //英雄所见... return CustomScrollView( slivers: [ _buildPersistentHeader(), //<-- 在列表上方创建PersistentHeader _buildSliverList() ], ); }
// 使用UnitPersistentHeaderDelegate创建PersistentHeader Widget _buildPersistentHeader() => SliverPersistentHeader( delegate: UnitPersistentHeaderDelegate());
```
3.SliverPersistentHeader的pinned与floating
属性
下面开始试验:
| pinnedtruefloatingfalse | pinnedtruefloatingtrue | | ------------------------------------------------------------ | ------------------------------------------------------------ | | |
| | 上滑:顶部会留出minExtent的高度,不再随上滑而减小 | 上滑:顶部会留出minExtent的高度,不再随上滑而减小 | | 下拉:直到滑到顶端时,剩余空间才会展开 | 下拉: 任意位置下拉时, 剩余空间会展开 |
下面开始试验:
| pinnedfalsefloatingfalse | pinnedfalsefloatingtrue | | ------------------------------------------------------------ | ------------------------------------------------------------ | | |
| | 上滑: 顶部会滑出 | 上滑:顶部会滑出 | | 下拉:直到滑到顶端时,顶部才会展开 | 下拉: 任意位置下拉时, 空间会展开 |
三、使用 SliverPersistentHeader
1. 封装PersistentHeaderBuilder
上面使用起来比较麻烦,可以自定义一个PersistentHeaderBuilder来简化构建
使用builder属性,将创建的逻辑移交到使用时,可以回调一些有价值的数据,比如偏移量
```dart 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
dart 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是可以共存的,如下
| 上滑 | 下拉 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | |
|
dart 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 ~