哈喽,我是老刘
前一篇文章我们讲了如何实现闲鱼首页的底部导航栏
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有自动化方式,不需要手动修改
代码如下
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State createState() => _HomePageState();
}
class _HomePageState extends State with TickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 10, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('闲鱼首页示例'),
),
body: CustomScrollView(
slivers: [
SliverAppBar(
// AppBar 1
),
const SliverAppBar(
// AppBar 2
),
SliverAppBar(
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: TabBar(
controller: _tabController,
isScrollable: true,
tabs: List.generate(
10,
(index) => Tab(text: 'Tab $index'),
),
),
titlePadding: EdgeInsets.zero,
background: const ColoredBox(color: Colors.green),
),
),
// 瀑布流布局
SliverGrid(
// ...
),
],
),
);
}
}
这里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开发手册》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。