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 _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), ), ), ); } }