一、GridView简介
GridView是一个可滚动的二维网格布局组件,用于展示多行多列的列表项。
二、GridView的创建方式
1. GridView.count - 固定列数
dart
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MainPage());
}
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
ScrollController _controller = ScrollController();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("GridView"),
),
body:GridView.count(
// scrollDirection: Axis.vertical, // 滚动方向 垂直方向
scrollDirection: Axis.horizontal, // 滚动方向 水平方向
padding: EdgeInsets.all(10), // 内边距
crossAxisCount: 5, // 列数
mainAxisSpacing: 10, // 行间距
crossAxisSpacing: 10, // 列间距
childAspectRatio: 1, // 宽高比
children: List.generate(100, (int index){
return Container(
color: Colors.red,
child: Text("第${index+1}个",
style: TextStyle(color: Colors.white,fontSize: 20),
),
);
}),
)
),
);
}
}
2. GridView.extent - 最大宽度
dart
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MainPage());
}
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
ScrollController _controller = ScrollController();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("GridView"),
),
body:GridView.extent(
maxCrossAxisExtent: 100, // 每个子项的最大宽度
mainAxisSpacing: 20, // 行间距
crossAxisSpacing: 20, // 列间距
childAspectRatio: 1, // 长宽比
padding: EdgeInsets.all(10), // 内边距
children: List.generate(20, (int index){
return Container(
color: Colors.amber,
child: Text("第${index+1}个",
style: TextStyle(color: Colors.white,fontSize: 20),
),
alignment: Alignment.center,
);
}),
)
),
);
}
}
3. GridView.builder - 动态构建(性能最优)
dart
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MainPage());
}
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
ScrollController _controller = ScrollController();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("GridView"),
),
body:GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 5,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 1,
),
itemBuilder: (BuildContext content,int index){
return Container(
color: Colors.orange,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.image,size: 30,),
Text("${index+1}",style: TextStyle(color:Colors.white,fontSize: 20),)
],
),
);
}
)
),
);
}
}
三、核心属性详解
1. gridDelegate - 网格布局代理
SliverGridDelegateWithFixedCrossAxisCount
less
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, // 固定列数
mainAxisSpacing: 10, // 主轴间距(垂直间距)
crossAxisSpacing: 10, // 交叉轴间距(水平间距)
childAspectRatio: 1.0, // 子项宽高比(宽度/高度)
)
SliverGridDelegateWithMaxCrossAxisExtent
less
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200, // 子项最大宽度
mainAxisSpacing: 10, // 行间距
crossAxisSpacing: 10, // 列间距
childAspectRatio: 1.0, // 长宽比
)
2. 滚动相关属性
less
GridView.builder(
physics: BouncingScrollPhysics(), // 滚动效果
shrinkWrap: false, // 是否根据内容收缩
reverse: false, // 是否反向滚动
controller: ScrollController(), // 滚动控制器
primary: true, // 是否使用主滚动视图
cacheExtent: 1000, // 缓存区域
addAutomaticKeepAlives: true, // 是否自动保持存活
addRepaintBoundaries: true, // 是否添加重绘边界
addSemanticIndexes: true, // 是否添加语义索引
// ... 其他属性
)
四、实际应用示例
1. 图片网格展示
scala
class ImageGrid extends StatelessWidget {
final List<String> imageUrls;
const ImageGrid({required this.imageUrls});
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: 0.8,
),
itemCount: imageUrls.length,
itemBuilder: (context, index) {
return ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
imageUrls[index],
fit: BoxFit.cover,
),
);
},
);
}
}
2. 卡片式网格
less
class CardGrid extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
padding: EdgeInsets.all(16),
childAspectRatio: 0.7,
children: List.generate(10, (index) {
return Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.vertical(
top: Radius.circular(12),
),
),
),
),
Padding(
padding: EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Title $index',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 4),
Text(
'Description here',
style: TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
),
],
),
);
}),
);
}
}
3. 瀑布流效果
less
class WaterfallGrid extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 0.8,
),
itemCount: 20,
itemBuilder: (context, index) {
// 随机高度效果
double height = 100 + (index % 5) * 30;
return Container(
height: height,
decoration: BoxDecoration(
color: Colors.primaries[index % Colors.primaries.length],
borderRadius: BorderRadius.circular(8),
),
child: Center(child: Text('Item $index')),
);
},
);
}
}
五、性能优化技巧
1. 使用builder模式
less
// ✅ 推荐:动态构建,只构建可见区域
GridView.builder(
itemCount: largeList.length,
itemBuilder: (context, index) => ItemWidget(item: largeList[index]),
)
// ❌ 不推荐:一次性构建所有子项
GridView.count(
children: largeList.map((item) => ItemWidget(item: item)).toList(),
)
2. 设置合适的缓存区域
less
GridView.builder(
cacheExtent: 500, // 缓存区域大小,默认为视口大小
// ...
)
3. 避免在build中创建对象
scala
class OptimizedGridView extends StatelessWidget {
// 提取delegate到外部避免重复创建
final SliverGridDelegate _delegate = SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
);
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: _delegate,
itemBuilder: _itemBuilder,
itemCount: 100,
);
}
Widget _itemBuilder(BuildContext context, int index) {
return Container(
// 使用const减少重建
color: Colors.blue,
child: Text('Item $index'),
);
}
}
六、常见问题及解决方案
1. GridView嵌套滚动问题
less
// 解决嵌套滚动冲突
SingleChildScrollView(
child: Column(
children: [
Text('Header'),
Container(
height: 300,
child: GridView.builder(
physics: NeverScrollableScrollPhysics(), // 禁用内部滚动
shrinkWrap: true, // 包裹内容
// ...
),
),
],
),
)
2. 动态改变列数
scala
class ResponsiveGridView extends StatelessWidget {
final int crossAxisCount;
const ResponsiveGridView({required this.crossAxisCount});
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
),
// ...
);
}
}
// 使用时
LayoutBuilder(
builder: (context, constraints) {
int columns = constraints.maxWidth > 600 ? 3 : 2;
return ResponsiveGridView(crossAxisCount: columns);
},
)
3. 空状态处理
scala
class GridViewWithEmptyState extends StatelessWidget {
final List<String> items;
@override
Widget build(BuildContext context) {
if (items.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.inbox, size: 64, color: Colors.grey),
Text('No items found', style: TextStyle(fontSize: 16)),
],
),
);
}
return GridView.builder(
itemCount: items.length,
// ...
);
}
}
七、总结
选择建议:
- GridView.count:简单场景,固定列数
- GridView.extent:响应式布局,自适应列宽
- GridView.builder:大量数据,需要性能优化
- GridView.custom:需要完全控制构建过程
注意事项:
- 大量数据时务必使用
builder模式 - 注意处理空状态和加载状态
- 合理设置
childAspectRatio保持布局美观 - 考虑不同屏幕尺寸的适配