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

168 lines
6.0 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';
/// ============================================================
/// GridView 网格布局演示页
///
/// 一、两种使用方式:
/// 1. 直接运行本页面:runApp(const MaterialApp(home: GridDemoPage()))
/// 2. 复制某段核心代码到任意 Scaffold 的 body 中。
///
/// 二、三个常用构造:
/// GridView.count : 固定列数,写死 children(一次性全部创建)
/// GridView.builder : 懒加载,itemCount + itemBuilder,大量数据首选
/// GridView.extent : 按"每项最大宽度"自适应列数(屏幕越宽列越多)
///
/// 三、核心参数:
/// crossAxisCount : 列数(GridView.count 专用)
/// maxCrossAxisExtent: 每项最大宽度(GridView.extent 专用)
/// mainAxisSpacing : 主轴(上下行)间距
/// crossAxisSpacing : 交叉轴(左右列)间距
/// childAspectRatio : 每项 宽:高 比例,默认 1(正方形)
///
/// 四、高频坑:
/// 1. GridView 放 Column 必须 SizedBox 定高或 Expanded(同 ListView);
/// 2. childAspectRatio 太小(如 0.5)时文字会被挤压溢出,调比例或缩小字号。
///
/// 五、和 CustomScrollView(Sliver) 的本质区别(重点记忆):
/// GridView = CustomScrollView + 1 个 SliverGrid(框架内部自动包的)。
/// SDK 源码:GridView extends BoxScrollView,而 ScrollView.build()
/// 返回的就是 CustomScrollView——所以 GridView 是"只有网格这一种内容"
/// 的简化套餐,两者都是"单个滚动 + 懒加载"。
/// · 页面只有网格 → body: GridView.builder(...) 直接用
/// · 页面要混排 AppBar + 网格 + 列表 → CustomScrollView + slivers
/// · 本页外层 SingleChildScrollView 只是"展示台":为并排演示 3 种构造,
/// 内部塞的是固定高 GridView(嵌套滚动)。真实网格页别学这个外层!
/// ============================================================
class GridDemoPage extends StatelessWidget {
const GridDemoPage({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(
appBar: AppBar(title: const Text('GridView 网格 Demo')),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _SectionTitle('Demo1:GridView.count 固定 3 列'),
Text(
'写死 children,一次性全创建,适合少量固定网格',
style: TextStyle(fontSize: 13.sp, color: Color(0xFF888888)),
),
SizedBox(height: 8.h),
SizedBox(
height: 260.h,
child: GridView.count(
crossAxisCount: 3, // 列数
mainAxisSpacing: 8.w, // 上下间距
crossAxisSpacing: 8.w, // 左右间距
childAspectRatio: 1, // 宽:高 = 1:1 正方形
children: [
for (int i = 0; i < 12; i++) _GridItem(index: i),
],
),
),
SizedBox(height: 32.h),
const _SectionTitle('Demo2:GridView.builder 懒加载'),
Text(
'itemCount: 100,只创建可见的格子,滚到哪建到哪',
style: TextStyle(fontSize: 13.sp, color: Color(0xFF888888)),
),
SizedBox(height: 8.h),
SizedBox(
height: 260.h,
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, // 4 列小格子
mainAxisSpacing: 8.w,
crossAxisSpacing: 8.w,
childAspectRatio: 1,
),
itemCount: 100, // 假装 100 个格子
itemBuilder: (context, index) => _GridItem(index: index),
),
),
SizedBox(height: 32.h),
const _SectionTitle('Demo3:GridView.extent 自适应列宽'),
Text(
'maxCrossAxisExtent: 120 → 每项最多 120 宽,屏幕越宽列数自动变多',
style: TextStyle(fontSize: 13.sp, color: Color(0xFF888888)),
),
SizedBox(height: 8.h),
SizedBox(
height: 260.h,
child: GridView.extent(
maxCrossAxisExtent: 80.w, // 每项最多 80 宽
mainAxisSpacing: 8.w,
crossAxisSpacing: 8.w,
childAspectRatio: 1,
children: [
for (int i = 0; i < 12; i++) _GridItem(index: i),
],
),
),
],
),
),
);
}
}
/// 单个网格项:彩色方块 + 序号
class _GridItem extends StatelessWidget {
const _GridItem({required this.index});
final int index;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: GridDemoPage._colors[index % GridDemoPage._colors.length],
borderRadius: BorderRadius.circular(8.r),
),
alignment: Alignment.center,
child: Text(
'$index',
style: TextStyle(
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
);
}
}
/// 区块标题
class _SectionTitle extends StatelessWidget {
const _SectionTitle(this.title);
final String title;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(bottom: 4.h),
child: Text(
title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
);
}
}