flutter架 构项目基础
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user