Flutter仿京东实战项目三-首页下拉刷新

726 阅读5分钟

好久好久没写代码了,由于最近可能要找工作,所以我准备找个项目写写练练手、找找写代码的感觉。由于我又是个喜新厌旧的人,所以准备找个我不熟的Flutter来写写。

京东我用的比较多,所以我准备用Flutter仿一个,尽量做到一比一还原功能

写这个文章的原则和内容

  • 类似简单布局的东西都是一带而过、毕竟这些东西我们只要多试试就能写对了
  • 能用现成的组件和开源库,就一定不自己写
  • 我很懒、不会讲具体代码实现,但是我会把思路步骤过程记录下来
  • 会记录下我实现功能中遇到的各种坑和有意思的地方
  • 我写的很随意,想到啥写到啥,可能会忘了很多东西,后来会补上

代码地址:github.com/nppp1990/jd…

因为代码还在更新,所以现在还是半成品;我会边更新代码边更新文章的

功能点

京东Android录屏(gif有点大、要等一会才看到哦)

876_1693146018.gif

下面是我实现的下拉首页录屏

878_1693148609.gif

  • 支持下拉刷新
  • 下拉时部分UI渐隐
    • 顶部header渐隐
    • 列表的分类头部渐隐
  • 下拉时背后的广告页渐现
  • 下拉到一定距离后:广告页随之往下移动
  • 下拉到更多距离后松手:广告页整体出现

功能分析和实现

  1. 整体页面的布局
  2. 下拉刷新
  3. 下拉刷新到二楼
  4. 下拉时渐隐动画

页面布局

show1.png
  1. 顶部header(浮动、渐隐)
  2. 中间可滑动部分(可滑动、可下拉)
  3. 底部固定的tabbar(固定)

下拉刷新组件

可选组件

  • Flutter内置的RefreshIndicator不支持下拉到“二楼”,刷新也不支持从顶部出现,所以不行
  • pub上搜索关键字pull refresh,pull_to_refresh这个库用的人最多,所以首选
image.png

pull_to_refresh支持市面上下拉刷新所需大部分的功能(包括下拉刷新、上拉加载、下拉到二楼),具体怎么用大家看官网即可

这里我贴下我的实现和注释

show2.png

下拉到二楼

这部分的实现、由于京东的下拉到二楼和pull_to_refresh的下拉二楼有很大的出入,所以需要修改源码

为了一比一还原,懒惰的我被迫看了很久的源码和试了很多次、才用了一个很hook的办法解决

尝试的过程需要熟悉flutter的sliver原理和不断地尝试,我也很菜,所以我就不介绍sliver了,这里推荐看这个Flutter - 循序渐进 Sliver

关于怎么看源码,我在修改下拉刷新代码时的经验是

  • 多断点
  • 一切源头都是从build函数开始
  • 多尝试各种可能、多打日志
  • 由于和滑动相关,更多地关注宽度、高度、坐标值xy这些变量的变化
  • 不要放过源码中的注释
  • 通过inspector看布局

源码帮大家看过了,先看看大致的原理

pull_to_refresh是通过CustomScrollView实现

UML 图 (2).jpg

京东首页下拉到第二层期望效果大致如下图

UML 图 (3).jpg

不同点如下

  1. 初始状态时:header和列表是偏下的
  2. 刚开始下拉时:列表往下移动、但是“二楼”的header是不移动的;而是随着盖在上面的列表header的消失、二楼header逐渐显现
  3. 下拉到一定距离后:“二楼”的header和列表一起向下滑动
如何修改初始状态时header和列表的位置呢

关于flutter工程如何源码依赖第三方库,可以看这个文档:dart.dev/tools/pub/d…

需要修改sliver.dart文件的RenderSliverRefreshperformLayout方法 image.png

上面代码实际上就是修改SliverGeometry对象的paintOriginlayoutExtent属性

SliverGeometry是什么呢:简单来说就是告诉sliver怎么布局、占位置的 image.png

paintOrigin更简单:就是相对于layout的起始位置,所以只需要把paintOrigin向下移即可 image.png

layoutExtent这个值决定了next sliver的layout的起始位置。所以如果不修改这个值,列表的sliver初始状态会在最顶部、所以需要加上offset image.png

如何实现重叠呢

paintOrigin下移多一点或者layoutExtent改大一些都可以,但是这么做后header会挡住list部分,也就是sliver1如果和sliver2重叠,重叠的部分会显示sliver1而不是sliver2

但是我期望的是list部分挡住header部分,所以这么做是不符合要求的。最终我用了一个hook的办法、实现了重叠的效果。

  1. 通过Stack布局,顶部放一个和二楼header底部UI一样的View1:高度为h1、开始时visible=false
  2. 刚开始下拉时,这个View1的visible改为true,二楼header的底部visible改为false、透明度为0
  3. 此时由于header和list的部分渐隐,就能看到View1了
  4. 当滑动到一定距离,View1和二楼底部UI重合时,修改彼此visible,这样就完美替身了

为了实现上述过程、还需要修改源码中的TwoLevelHeaderouterBuilder函数。源码中给header添加刷新的思路是加外层套一个stack(refresh+headeer的背景图)来实现,我这里不是图、所以改成了stack(refresh+headeer的layout

渐隐动画的实现
  1. 顶部悬浮的header用Opacity包起来
  2. 列表页顶部的筛选category部分用Opacity包起来
  3. 放在layout后面的View1用Offstage控制visible
  4. 二楼的header底部View1部分用Offstage控制visible
  5. 监听Refresh滑动的距离和状态
  6. 通过Provider通知Opacity和Visible的变化

下图为监听refresh滑动的代码 image.png

下图为接受opacity变化的代码 image.png

都看到最后了,不给个三连么😄