好久好久没写代码了,由于最近可能要找工作,所以我准备找个项目写写练练手、找找写代码的感觉。由于我又是个喜新厌旧的人,所以准备找个我不熟的Flutter来写写。
京东我用的比较多,所以我准备用Flutter仿一个,尽量做到一比一还原功能
写这个文章的原则和内容
- 类似简单布局的东西都是一带而过、毕竟这些东西我们只要多试试就能写对了
- 能用现成的组件和开源库,就一定不自己写
- 我很懒、不会讲具体代码实现,但是我会把思路和步骤过程记录下来
- 会记录下我实现功能中遇到的各种坑和有意思的地方
- 我写的很随意,想到啥写到啥,可能会忘了很多东西,后来会补上
因为代码还在更新,所以现在还是半成品;我会边更新代码边更新文章的
功能点
京东Android录屏(gif有点大、要等一会才看到哦)
下面是我实现的下拉首页录屏
- 支持下拉刷新
- 下拉时部分UI渐隐
- 顶部header渐隐
- 列表的分类头部渐隐
- 下拉时背后的广告页渐现
- 下拉到一定距离后:广告页随之往下移动
- 下拉到更多距离后松手:广告页整体出现
功能分析和实现
- 整体页面的布局
- 下拉刷新
- 下拉刷新到二楼
- 下拉时渐隐动画
页面布局
- 顶部header(浮动、渐隐)
- 中间可滑动部分(可滑动、可下拉)
- 底部固定的tabbar(固定)
下拉刷新组件
可选组件
- Flutter内置的
RefreshIndicator:不支持下拉到“二楼”,刷新也不支持从顶部出现,所以不行 - pub上搜索关键字pull refresh,pull_to_refresh这个库用的人最多,所以首选
pull_to_refresh支持市面上下拉刷新所需大部分的功能(包括下拉刷新、上拉加载、下拉到二楼),具体怎么用大家看官网即可
这里我贴下我的实现和注释
下拉到二楼
这部分的实现、由于京东的下拉到二楼和pull_to_refresh的下拉二楼有很大的出入,所以需要修改源码
为了一比一还原,懒惰的我被迫看了很久的源码和试了很多次、才用了一个很hook的办法解决
尝试的过程需要熟悉flutter的sliver原理和不断地尝试,我也很菜,所以我就不介绍sliver了,这里推荐看这个Flutter - 循序渐进 Sliver
关于怎么看源码,我在修改下拉刷新代码时的经验是
- 多断点
- 一切源头都是从
build函数开始 - 多尝试各种可能、多打日志
- 由于和滑动相关,更多地关注宽度、高度、坐标值xy这些变量的变化
- 不要放过源码中的注释
- 通过inspector看布局
源码帮大家看过了,先看看大致的原理
pull_to_refresh是通过CustomScrollView实现
京东首页下拉到第二层期望效果大致如下图
不同点如下
- 初始状态时:header和列表是偏下的
- 刚开始下拉时:列表往下移动、但是“二楼”的header是不移动的;而是随着盖在上面的列表header的消失、二楼header逐渐显现
- 下拉到一定距离后:“二楼”的header和列表一起向下滑动
如何修改初始状态时header和列表的位置呢
关于flutter工程如何源码依赖第三方库,可以看这个文档:dart.dev/tools/pub/d…
需要修改sliver.dart文件的RenderSliverRefresh的performLayout方法
上面代码实际上就是修改SliverGeometry对象的paintOrigin和layoutExtent属性
SliverGeometry是什么呢:简单来说就是告诉sliver怎么布局、占位置的
paintOrigin更简单:就是相对于layout的起始位置,所以只需要把paintOrigin向下移即可
layoutExtent这个值决定了next sliver的layout的起始位置。所以如果不修改这个值,列表的sliver初始状态会在最顶部、所以需要加上offset
如何实现重叠呢
paintOrigin下移多一点或者layoutExtent改大一些都可以,但是这么做后header会挡住list部分,也就是sliver1如果和sliver2重叠,重叠的部分会显示sliver1而不是sliver2
但是我期望的是list部分挡住header部分,所以这么做是不符合要求的。最终我用了一个hook的办法、实现了重叠的效果。
- 通过Stack布局,顶部放一个和二楼header底部UI一样的View1:高度为h1、开始时visible=false
- 刚开始下拉时,这个View1的visible改为true,二楼header的底部visible改为false、透明度为0
- 此时由于header和list的部分渐隐,就能看到View1了
- 当滑动到一定距离,View1和二楼底部UI重合时,修改彼此visible,这样就完美替身了
为了实现上述过程、还需要修改源码中的TwoLevelHeader的outerBuilder函数。源码中给header添加刷新的思路是加外层套一个stack(refresh+headeer的背景图)来实现,我这里不是图、所以改成了stack(refresh+headeer的layout)
渐隐动画的实现
- 顶部悬浮的header用
Opacity包起来 - 列表页顶部的筛选category部分用
Opacity包起来 - 放在layout后面的View1用
Offstage控制visible - 二楼的header底部View1部分用
Offstage控制visible - 监听Refresh滑动的距离和状态
- 通过Provider通知Opacity和Visible的变化
- Provider如何使用我参考的这个Flutter状态管理之 Provider 使用详解
- 通过ScrollController.addListener来监听refresh的滑动
- 通过RefreshController.headerStatus来获取refresh的状态
下图为监听refresh滑动的代码
下图为接受opacity变化的代码
都看到最后了,不给个三连么😄