我做 Flutter 页面的时候,最常写的就是列表:消息流、设置页、商品流、相册……几乎所有“内容型页面”,最后都会落到 ListView 或 GridView 上。
这篇文章不打算把 API 手册从头抄一遍,我更想写成那种“我平时怎么用、为什么这么写、哪里容易翻车”的小结,给你一个可直接套用的思路。
先选对组件:ListView 还是 GridView?
简单到有点粗暴,但很好用:
- 一行一个 →
ListView - 一屏多个卡片(二维排列)→
GridView
它们本质都是可滚动的布局容器。差别主要在“子项怎么排”:List 是单列/单行,Grid 是二维网格。
ListView:从“能跑”到“跑得顺”
1)ListView(children: ...) 适合短列表
如果你只有十几个固定项(比如设置页),直接写 children 最省心:
1 | ListView( |
但只要数据一多,我会立刻切换到 builder。
2)长列表优先 ListView.builder
builder 的核心价值是:只构建屏幕上看得到的那部分。这点在图片、复杂卡片里非常关键。
1 | ListView.builder( |
3)我很常用:ListView.separated
很多列表就是“内容 + 分割线”,separated 省得你自己在 item 里判断 index:
1 | ListView.separated( |
4)两个常见坑(我踩过不止一次)
坑 A:把 ListView 塞进 Column,然后直接报错/溢出
一般是因为 ListView 想要“无限高”,但 Column 给不了。我的习惯是:
- 能用整屏滚动:直接让
ListView做 body - 必须上下还有别的固定区域:用
Expanded包起来
1 | Column( |
坑 B:shrinkWrap: true 滥用导致卡顿
shrinkWrap 会让列表去“量身高”,代价就是额外计算和布局;在嵌套滚动里它有时不得不用,但我会把它当成“最后手段”,并尽量改成 CustomScrollView(下面会提)。
GridView:把网格写顺手
1)最常用的两种 gridDelegate
按列数固定:SliverGridDelegateWithFixedCrossAxisCount
适合“每行就是 2/3/4 个卡片”的设计:
1 | GridView.builder( |
按宽度自适应:SliverGridDelegateWithMaxCrossAxisExtent
适合不同屏幕尺寸下“尽量多排,但别挤太窄”:
1 | GridView.builder( |
我选它的理由很简单:你不用为“iPhone / iPad / 横屏”分别写 crossAxisCount,视觉更稳。
一个更通用的解法:CustomScrollView + Sliver(混排神器)
当你遇到这种页面:
- 顶部一块 banner(可滚动)
- 下面先来一段横向分类(可滚动)
- 再接一个纵向列表 / 网格
这时候我会直接上 CustomScrollView,用 SliverList / SliverGrid 混在一起,不用再把 ListView/GridView 套来套去。
1 | CustomScrollView( |
我现在做“内容流页面”,基本都靠它吃饭。
我自己的小清单(写完会顺很多)
- 数据多:优先
builder(ListView.builder/GridView.builder) - 尽量减少每个 item 的“重活”:图片、阴影、复杂布局能简则简
- 列表高度固定时,考虑
itemExtent/prototypeItem(能省不少布局成本) - 避免把多个可滚动组件互相嵌套;需要混排时直接用
CustomScrollView + Sliver - 有状态的 item(比如点赞/展开)记得用合适的
Key,否则滚动复用时容易“串戏”
结尾
ListView 和 GridView 其实不难,难的是在真实页面里:既要写得顺,又要滑得顺。