开发时遇到需要实现粘性头部效果,类似iOS端的那种。本着有轮子就不造轮子的原则,先在Pub仓库搜索一番,不出所料,果然有大佬已经造好了轮子,心里美滋滋,今天又能早点下班,Demo跑一跑,改一改完事~
事实证明,啥事都不能开心的太早。几个Demo跑完,和我实际想要的有点差距,易用的功能单一,功能强一点的用起来麻烦。没办法,只能加班自己搞一个。具体实现原理和遇到的坑后面再慢慢一一道来,先把菜上了,让大家尝尝味道。
添加依赖:
dependencies:
easy_sticky_header: ^1.0.5
导入库:
import 'package:easy_sticky_header/easy_sticky_header.dart';
开始使用:
StickyHeader
包裹任何可以滚动的组件,例如ListView、GridView、CustomScrollView、SingleChildScrollView等StickyContainerWidget
包裹自定义的头部组件,指定index即可。注意,index不要求连续,但必须唯一且顺序是从小到大的,推荐使用滚动组件提供的indexclass Example extends StatelessWidget {
const Example({super.key});
Widget build(BuildContext context) {
return StickyHeader(
child: ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
// 自定义你的头部组件
if (index % 3 == 0) {
return StickyContainerWidget(
index: index,
child: Container(
color: Color.fromRGBO(Random().nextInt(256),
Random().nextInt(256), Random().nextInt(256), 1),
padding: const EdgeInsets.only(left: 16.0),
alignment: Alignment.centerLeft,
width: double.infinity,
height: 50,
child: Text(
'Header #$index',
style: const TextStyle(
color: Colors.white,
fontSize: 16,
),
),
),
);
}
// 自定义你的Item
return Container(
width: double.infinity,
height: 80,
color: Colors.white,
);
},
),
);
}
}
效果展示:
粘性量可以用于实现类似微信通讯录分组头部切换的过渡动画效果。
StickyContainerBuilder
通过回调动态构建头部,当stickyAmount
没有改变时不会重新构建stickyAmount
stickyAmount
,如果使用了StickyContainerBuilder
组件会自动开启,其他情况也想开启的话,手动设置useStickyAmount
为true。class Example extends StatelessWidget {
const Example({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return StickyHeader(
child: ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
// 自定义你的头部组件
if (index % 3 == 0) {
return StickyContainerBuilder(
index: index,
builder: (context, stickyAmount) => Container(
// 根据粘性量动态改变颜色,实现过渡效果
color: Color.fromRGBO(
155 + (100 * stickyAmount).toInt(), 105, 0, 1.0),
padding: const EdgeInsets.only(left: 16.0),
alignment: Alignment.centerLeft,
width: double.infinity,
height: min(100, 50 + 5.0 * index),
child: Text(
'Header #$index stickyAmount:${stickyAmount.toStringAsFixed(2)}',
style: const TextStyle(
color: Colors.white,
fontSize: 16,
),
),
),
);
}
// 自定义你的Item
return Column(
children: <Widget>[
Container(
width: double.infinity,
height: 80,
color: Colors.white,
padding: const EdgeInsets.only(left: 16),
alignment: Alignment.centerLeft,
child: Text(
'Item #$index',
style: const TextStyle(
color: Colors.black,
fontSize: 16,
),
),
),
Divider(
height: 1.0,
thickness: 1.0,
color: Colors.grey.shade200,
indent: 16.0,
),
],
);
},
),
);
}
}
效果展示:
众所周知,在Flutter中,不依赖第三方库跳转到列表指定Item不是一件容易的事(如果全部Item高度一致,当我没说)。在实现这个库的过程中,因为有缓存,所以可以很简单地跳转已经在缓存中的头部,但是局限性很大,对于还未构建过的头部组件,就无能为力了。
这么核心的一个功能,如果都没实现,那用起来是真难受。现在从1.0.3版本开始,通过查找的方式已经实现任意头部跳转,原理后面再说,先看效果展示:
完整源码详见示例项目-Example5。
核心源码:
void animateTo(
// 跳转目标的头部索引
int index, {
// 偏移量,有时需要设置一点点偏移量让头部组件成为粘性头部(详见示例源码)
double offset = 0.0,
// 滚动速度,单位:像素/毫秒,设为1.0即一秒滚动1000像素
double velocity = 1.0,
// 可以直接跳转时使用的动画参数
Duration? duration,
Curve? curve,
// 通过查找跳转时使用的动画参数
Duration? findingStartDuration,
Curve? findingStartCurve,
Duration? findingEndDuration,
Curve? findingEndCurve,
})
有了头部跳转这一核心功能后,配合字母索引条(列),就可以轻松实现通讯录、城市列表等功能啦。
头部分组指多个头部可以组成一个组,粘性头部的构建内容由父头部提供。说的可能不是很清楚,但这个使用效果,诸位应该都见过,先看效果展示:
紫色背景的父头部:由TabBar组件组成。当子头部成为粘性头部时,切换指示器。点击tab时,跳转到对应的子头部。
绿色背景的子头部:只需要指定父头部索引,通过索引关联父头部。
核心源码:
// ParentStickyContainerBuilder用于构建父头部,如果你非要拿去构建子头部,那也不是不行♂️
// 注意:StickyContainerWidget和StickyContainerBuilder也可以构建父头部,只是没有ParentStickyContainerBuilder那么方便快捷
ParentStickyContainerBuilder({
Key? key,
required this.index,
this.visible = true,
this.pixels,
this.performancePriority = true,
// 更新回调,如果父头部组件不需要一直重新构建,可以在回调中返回false(参考示例源码)
this.onUpdate,
// 通过当前粘性子头部信息构建父头部组件
required this.builder,
})
// StickyContainerWidget构建子头部,StickyContainerBuilder也可以
StickyContainerWidget({
Key? key,
required this.index,
this.visible = true,
this.pixels,
this.performancePriority = true,
// 指定父头部索引
this.parentIndex,
// 是否重叠父头部,一般来说都是不重叠的,使用默认值即可
this.overlapParent = false,
required Widget child,
})
看到这,诸位对这个项目应该有所了解了,使用起来还是很容易的。如果想自定义类似StickyContainerBuilder
这样动态构建的头部,请参考sticky_container_widget里面的实现方式。
更多使用例子请参考示例项目,诸多应用场景都有例子可参考,目前也还在不断更新中,欢迎留言补充使用场景。
如果你有兴趣了解项目的实现原理和遇到的问题,欢迎查看后续文章。
如果这个项目对你有所帮助,点赞加星安排一下~
如果你发现了bug或想要新功能,欢迎在issue留言讨论,同时也欢迎你加入到这个项目中,为这项目出一份力。
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(一)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(二)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(三)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(四)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(五)