115 lines
4.1 KiB
Markdown
115 lines
4.1 KiB
Markdown
# 【Demo】CustomScrollView + Sliver 混排布局
|
||
|
||
> 源文件:`lib/pages/demo/sliver_demo.dart` | 分类:基础布局
|
||
> 一句话:折叠 AppBar + 网格 + 长列表"整页只滚一次",靠 Sliver 碎片拼装。
|
||
|
||
---
|
||
|
||
## 一、这个 Demo 解决什么问题
|
||
|
||
页面要同时包含"可折叠的大图 AppBar + 网格区块 + 长列表区块"时:
|
||
- **ListView 不能嵌套 ListView/GridView**(同向滚动会冲突,控制权打架);
|
||
- 正解是 `CustomScrollView`:把所有区块变成 Sliver"碎片"拼在一起,**共用一个滚动控制器,整页只滚一次**。
|
||
|
||
---
|
||
|
||
## 二、怎么运行 / 演示内容
|
||
|
||
```dart
|
||
runApp(const MaterialApp(home: SliverDemoPage()));
|
||
```
|
||
|
||
页面从上到下:折叠大图 AppBar → 网格标题 → 12 格网格 → 列表标题 → 50 条列表,
|
||
下滑时 AppBar 收起固定,其余内容连续滚动。
|
||
|
||
---
|
||
|
||
## 三、核心知识点(新手向)
|
||
|
||
### 1. Sliver 是什么
|
||
Sliver = "碎片"。`CustomScrollView` 把多个 sliver 拼成一个统一滚动视图。
|
||
|
||
### 2. 常用 sliver 对照表
|
||
| Sliver | 作用 | 对应普通组件 |
|
||
|---|---|---|
|
||
| `SliverAppBar` | 可折叠/固定的 AppBar | AppBar |
|
||
| `SliverToBoxAdapter` | 把普通组件(文字/卡片)塞进滚动流 | 任意 Widget |
|
||
| `SliverGrid.builder` | 网格区块(懒加载) | GridView |
|
||
| `SliverList.builder` | 列表区块(懒加载) | ListView.builder |
|
||
| `SliverPadding` | 给某个 sliver 加内边距 | Padding |
|
||
|
||
### 3. 核心结构
|
||
```dart
|
||
CustomScrollView(
|
||
slivers: [
|
||
SliverAppBar( // ① 折叠 AppBar
|
||
expandedHeight: 180.h,
|
||
pinned: true, // 滚动时固定在顶部
|
||
flexibleSpace: FlexibleSpaceBar(
|
||
title: Text('Sliver 系列 Demo'),
|
||
background: DecoratedBox(...), // 展开时的大图
|
||
),
|
||
),
|
||
SliverToBoxAdapter( // ② 普通文字标题
|
||
child: Padding(...Text('网格区块(SliverGrid)')),
|
||
),
|
||
SliverPadding( // ③ 网格区块(带外边距)
|
||
padding: EdgeInsets.all(16.w),
|
||
sliver: SliverGrid.builder(...),
|
||
),
|
||
SliverList.builder( // ④ 列表区块
|
||
itemCount: 50,
|
||
itemBuilder: ...,
|
||
),
|
||
],
|
||
)
|
||
```
|
||
|
||
### 4. 判断口诀(什么时候用 Sliver)
|
||
- 页面是"**一个网格**"或"**一个列表**" → `GridView` / `ListView` 更简单;
|
||
- 页面是"**折叠头 + 网格 + 长列表**"混排 → `CustomScrollView + slivers`。
|
||
|
||
---
|
||
|
||
## 四、关键代码剖析
|
||
|
||
### 折叠 AppBar(本页的灵魂)
|
||
```dart
|
||
SliverAppBar(
|
||
expandedHeight: 180.h, // 展开时高度(大图区域)
|
||
pinned: true, // 上滑时缩成一条标题栏并固定在顶部
|
||
backgroundColor: Color(0xFF3B82F6),
|
||
flexibleSpace: FlexibleSpaceBar(
|
||
title: Text('Sliver 系列 Demo'),
|
||
centerTitle: true,
|
||
background: DecoratedBox( // 展开时显示的大图(这里是渐变)
|
||
decoration: BoxDecoration(
|
||
gradient: LinearGradient(
|
||
colors: [Color(0xFF3B82F6), Color(0xFF8E6FE8)],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## 五、踩坑记录
|
||
|
||
1. **slivers 里的组件必须是 Sliver 家族**,普通 Widget(如 Container)直接放会报类型错误——
|
||
要用 `SliverToBoxAdapter` 包一层;
|
||
2. **同向嵌套滚动是禁区**:CustomScrollView 里不能再放 ListView/GridView;
|
||
3. `SliverAppBar` 不设 `pinned` 时,上滑后 AppBar 会完全滚出屏幕(floating 可以浮回)——按产品需求选;
|
||
4. Sliver 的懒加载与 GridView/ListView 相同:builder 系列才懒加载。
|
||
|
||
---
|
||
|
||
## 六、扩展思考
|
||
|
||
- **Sliver 与 GridView 的本质关系**(重点记忆):
|
||
`GridView = CustomScrollView + 1 个 SliverGrid`,两者都是"单个滚动 + 懒加载",区别只在混排能力;
|
||
- 进阶 Sliver:`SliverPersistentHeader`(吸顶 Tab)、`SliverAnimatedList`(增删动画列表)、`SliverAppBar` 的 `snap` 模式;
|
||
- 电商首页 = Sliver 混排的教科书场景:搜索栏吸顶 + 轮播 + 宫格 + 瀑布流商品;
|
||
- 对照 [responsive_demo.md](responsive_demo.md):Sliver 解决"滚动结构",响应式解决"不同宽度下的结构",两者可叠加。
|