Flutter如何优雅地实现闲鱼首页嵌套滑动
哈喽,我是老刘
前一篇文章我们讲了如何实现闲鱼首页的底部导航栏 Flutter如何优雅的实现闲鱼首页底部导航栏 - 知乎 (zhihu.com)
今天我们来讲解如何实现闲鱼首页的页面内容 其实主要就是各种不同滑动效果的嵌套
我们先来看一下整个页面的结构
页面结构分析
其实不考虑底下的导航栏的话,页面总体可以分为两部分: 上面第一部分是固定的头部 这部分是不随着滑动变化的,搜索栏点击后会跳转新页面 所以整个第一部分很简单,就不赘述了
第二部分整体是一个可以上下滑动的ListView 这个ListView内又可以分为4部分
第一行:可以横向滑动的按钮栏
第二行:可以横向滑动的卡片栏 整体来说这个和第一行是一样的 但是有一点略微复杂 如果大家仔细观察就会发现,卡片在滑动时,其中的图片会有一个动画效果 动画的原理我们讲过 每一帧都会调用一个你写的回调,传入滑动偏移量等信息 所以每一帧都在回调中根据滑动偏移量计算图片的大小及位置即可 这个也不复杂,就不展开讲了 如果有同学需要讲解这部分,可以评论区留言,我到时候再单独写篇文章
第三行:分类的tab栏 这个也是可以横向滑动的 但是这里有一个特殊效果要注意, 就是随着向上滑动,最终这个tab栏会吸顶
第四行:纵向滑动的内容瀑布流
所以前三行其实都是横向滑动的效果,嵌入到一个纵向滑动的ListView中 接下来我们来一步步实现这个页面效果
实现页面框架
首先来实现一下顶部三行?瀑布流的页面框架 今天这个方案可能稍微有一点不正规 但是它非常符合我们正常的思维逻辑
我们知道在Flutter中,如果想实现多个ListView组合嵌套的效果,通常可以用Sliver系列组件实现 而想要实现吸顶效果,可以使用SliverAppBar组件 那么瀑布流上面的三行内容是不是可以通过放置三个SliverAppBar来实现呢? 我们来试一下
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('闲鱼首页示例'),
),
body: CustomScrollView(
slivers: <Widget>[
const SliverAppBar(
flexibleSpace: FlexibleSpaceBar(
title: Text('AppBar 1'),
background: ColoredBox(color: Colors.orange),
),
),
const SliverAppBar(
flexibleSpace: FlexibleSpaceBar(
title: Text('AppBar 2'),
background: ColoredBox(color: Colors.yellow),
),
),
const SliverAppBar(
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('AppBar 3 吸顶效果'),
background: ColoredBox(color: Colors.green),
),
),
// 瀑布流布局
SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 0.7,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
color: Colors.primaries[index % Colors.primaries.length],
child: Center(
child: Text('Item $index'),
),
);
},
childCount: 30, // 假设有30个item
),
),
],
),
);
}
}
我们通过CustomScrollView放置三个SliverAppBar和一个基于SliverGrid实现的瀑布流
其中第三个SliverAppBar设置了吸顶
来看一下效果
可以看到,同时放置三个SliverAppBar是没有问题的 但是要注意,SliverAppBar 本身支持多种效果比如悬浮、缩放等等 这些效果都是针对单一AppBar的场景设计的 当我们同时放置多个SliverAppBar时一定要注意各种特效的冲突 这也是我前面说这种用法稍微有点不正规的原因
接下来就要把AppBar中的内容改成横向滑动的列表
SliverAppBar 中的横向滑动
我们仔细观察这三行横向滑动部分会发现 其实前两行只是按钮,点击后会跳转到新页面 而第三行实际上是一个TabBar,点击后会改变瀑布流的内容 所以这里我们可以用两种方案来实现横向滑动效果
前两行的横向滑动按钮栏,可以通过ListView来实现
SliverAppBar(
flexibleSpace: FlexibleSpaceBar(
title: ListView.builder(
scrollDirection: Axis.horizontal, // 设置ListView为横向滑动
itemCount: 10,
itemBuilder: (context, index) {
return Container(
alignment: Alignment.center,
padding: EdgeInsets.all(10),
child: Text('Button $index'),
);
},
),
titlePadding: EdgeInsets.zero,
background: const ColoredBox(color: Colors.orange),
),
)
把第一个 SliverAppBar 的 title 部分替换成ListView
要注意 title 部分默认带有Padding,所以需要通过 titlePadding 属性去掉缩进
来看一下效果
好的,这样前两行的横向滑动效果就有了 只需要把 Container 中的内容替换成按钮并增加点击事件即可
下面我们来看一下第三行的TabBar
TabBar 需要配合 TabController 使用
所以需要先把 HomePage 的类型从 StatelessWidget 替换为 StatefulWidget
这个操作IDE有自动化方式,不需要手动修改
代码如下
这里TabBar的使用就不详细赘述了
来看下效果
那么到这里整个页面瀑布流上方的三个横向滑动栏就完成了
接下来我们看看瀑布流部分如何实现
瀑布流
好吧,实现瀑布流比较麻烦 这里偷个懒吧,直接找三方库 现在 Flutter 生态很不错,不需要重复发明轮子 不过这里要注意,需要找支持 Sliver 的三方库 我们这里以 waterfall_flow 库为例 waterfall_flow | Flutter package (pub.dev)
把瀑布流部分替换成如下代码:
SliverWaterfallFlow(
gridDelegate: const SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // 设置列数为2
crossAxisSpacing: 10.0, // 水平间距
mainAxisSpacing: 10.0, // 垂直间距
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
height: 50.0 + 40.0 * (index % 9), // 每个元素高度不固定
child: Text('grid item $index'));
},
childCount: 20,
),
)
这里使用了库中的 SliverWaterfallFlow 组件 看一下效果
好了,到这里整个页面的内容部分就全部完成了 如果结合前面一篇文章讲到的底部导航栏 我们就实现了闲鱼首页的效果
如果看到这里的同学有学习Flutter的兴趣,欢迎联系老刘,我们互相学习。 点击免费领老刘整理的《Flutter开发手册》,覆盖90%应用开发场景。 可以作为Flutter学习的知识地图。 覆盖90%开发场景的《Flutter开发手册》
转载自:https://juejin.cn/post/7372137873976459327