Files
2026-09-04 15:22:20 +08:00

158 lines
5.5 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
/// ============================================================
/// CustomScrollView + Sliver 系列演示页
///
/// 一、两种使用方式:
/// 1. 直接运行本页面:runApp(const MaterialApp(home: SliverDemoPage()))
/// 2. 复制 slivers 数组到任意 CustomScrollView 中。
///
/// 二、核心概念:
/// Sliver = "碎片"。CustomScrollView 把多个 sliver 拼成一个
/// 统一的滚动视图(共用一个滚动控制器,整页一起滚)。
///
/// 三、常用 sliver:
/// SliverAppBar : 可折叠/固定的 AppBar(pinned 固定、floating 浮出)
/// SliverToBoxAdapter : 把普通组件(文字/卡片/图片)塞进滚动流
/// SliverGrid.builder : 网格区块(懒加载)
/// SliverList.builder : 列表区块(懒加载)
/// SliverPadding : 给某个 sliver 加内边距
///
/// 四、为什么需要 Sliver(重点):
/// ListView 不能嵌套 ListView/GridView(同向滚动会冲突);
/// Sliver 方案让"AppBar + 网格 + 列表 + 卡片"混排且整页只滚一次。
///
/// 五、和 GridView 的本质区别(重点记忆):
/// GridView 只是 CustomScrollView 的简化套餐:
/// GridView = CustomScrollView + 1 个 SliverGrid。
/// 两者都是"单个滚动 + 懒加载",区别只在混排能力:
/// · 页面只有一种内容(网格/列表)→ GridView / ListView,写起来更简单
/// · 页面要混排多种内容 → 本页这种 CustomScrollView,slivers 拆块拼装
/// 判断口诀:这页是"一个网格"还是"折叠头+网格+长列表"?后者才用 Sliver。
/// ============================================================
class SliverDemoPage extends StatelessWidget {
const SliverDemoPage({super.key});
static const List<Color> _colors = [
Color(0xFF3B82F6), Color(0xFF8E6FE8), Color(0xFFF05252),
Color(0xFFFF9800), Color(0xFF4CAF50), Color(0xFF00BCD4),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
// ① 可折叠 AppBar:下拉展开大图,上滑收起只留标题栏
SliverAppBar(
expandedHeight: 180.h, // 展开时高度
pinned: true, // 滚动时固定在顶部
backgroundColor: Color(0xFF3B82F6),
flexibleSpace: FlexibleSpaceBar(
title: Text('Sliver 系列 Demo'),
centerTitle: true,
background: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFF3B82F6), Color(0xFF8E6FE8)],
),
),
),
),
),
// ② 普通组件插进滚动流:SliverToBoxAdapter
SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.fromLTRB(16.w, 16.h, 16.w, 0),
child: Text(
'网格区块(SliverGrid,懒加载)',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
),
),
// ③ 网格区块
SliverPadding(
padding: EdgeInsets.all(16.w),
sliver: SliverGrid.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 8.w,
crossAxisSpacing: 8.w,
),
itemCount: 12,
itemBuilder: (context, index) => _GridItem(index: index),
),
),
// ④ 区块标题
SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.fromLTRB(16.w, 8.h, 16.w, 8.h),
child: Text(
'列表区块(SliverList,懒加载)',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
),
),
// ⑤ 列表区块:50 条,和上面的网格共用同一个滚动
SliverList.builder(
itemCount: 50,
itemBuilder: (context, index) => ListTile(
leading: CircleAvatar(
backgroundColor: _colors[index % _colors.length],
child: Text(
'$index',
style: const TextStyle(color: Colors.white),
),
),
title: Text('第 $index 条'),
subtitle: Text('Sliver 懒加载,滚动到这里才创建'),
trailing: const Icon(Icons.chevron_right),
),
),
],
),
);
}
}
/// 网格项:彩色方块 + 序号
class _GridItem extends StatelessWidget {
const _GridItem({required this.index});
final int index;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: SliverDemoPage._colors[index % SliverDemoPage._colors.length],
borderRadius: BorderRadius.circular(8.r),
),
alignment: Alignment.center,
child: Text(
'$index',
style: TextStyle(
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
);
}
}