Flutter GridView 网格组件完全指南
·
什么是 GridView?
GridView 是 Flutter 中用于显示网格布局的组件,可以创建多列的滚动列表。
用途:
- 图片墙
- 商品展示
- 应用图标网格
- 相册
- 任何需要网格布局的场景
GridView 的类型
1. GridView.count - 固定列数
GridView.count(
crossAxisCount: 3, // 3 列
children: List.generate(20, (index) {
return Container(
color: Colors.blue,
child: Center(child: Text('$index')),
);
}),
)
2. GridView.extent - 固定最大宽度
GridView.extent(
maxCrossAxisExtent: 150, // 每个格子最大宽度 150
children: List.generate(20, (index) {
return Container(
color: Colors.green,
child: Center(child: Text('$index')),
);
}),
)
3. GridView.builder - 动态网格(推荐)
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemCount: 20,
itemBuilder: (context, index) {
return Container(
color: Colors.red,
child: Center(child: Text('$index')),
);
},
)
常用属性
1. gridDelegate - 网格代理
// 固定列数
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, // 列数
mainAxisSpacing: 10, // 主轴间距
crossAxisSpacing: 10, // 交叉轴间距
childAspectRatio: 1.0, // 宽高比
),
itemBuilder: (context, index) {
return Container(color: Colors.blue);
},
)
// 固定最大宽度
GridView.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150, // 最大宽度
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 1.0,
),
itemBuilder: (context, index) {
return Container(color: Colors.green);
},
)
2. padding - 内边距
GridView.count(
crossAxisCount: 3,
padding: EdgeInsets.all(10),
children: [...],
)
3. scrollDirection - 滚动方向
// 垂直滚动(默认)
GridView.count(
scrollDirection: Axis.vertical,
crossAxisCount: 3,
children: [...],
)
// 水平滚动
GridView.count(
scrollDirection: Axis.horizontal,
crossAxisCount: 3,
children: [...],
)
实战案例
案例1:图片网格
class ImageGrid extends StatelessWidget {
final List<String> images = List.generate(
20,
(index) => 'https://picsum.photos/200?random=$index',
);
Widget build(BuildContext context) {
return GridView.builder(
padding: EdgeInsets.all(10),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: images.length,
itemBuilder: (context, index) {
return ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
images[index],
fit: BoxFit.cover,
),
);
},
);
}
}
案例2:商品网格
class Product {
final String name;
final String image;
final double price;
Product({required this.name, required this.image, required this.price});
}
class ProductGrid extends StatelessWidget {
final List<Product> products = [
Product(name: 'iPhone 15', image: 'url', price: 7999),
Product(name: 'MacBook', image: 'url', price: 12999),
// 更多商品...
];
Widget build(BuildContext context) {
return GridView.builder(
padding: EdgeInsets.all(10),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 0.7,
),
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.vertical(top: Radius.circular(4)),
child: Image.network(
product.image,
width: double.infinity,
fit: BoxFit.cover,
),
),
),
Padding(
padding: EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: TextStyle(fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 4),
Text(
'¥${product.price}',
style: TextStyle(
color: Colors.red,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
);
},
);
}
}
案例3:应用图标网格
class AppIconGrid extends StatelessWidget {
final List<Map<String, dynamic>> apps = [
{'icon': Icons.camera, 'name': '相机', 'color': Colors.blue},
{'icon': Icons.photo, 'name': '相册', 'color': Colors.green},
{'icon': Icons.music_note, 'name': '音乐', 'color': Colors.red},
{'icon': Icons.video_library, 'name': '视频', 'color': Colors.orange},
{'icon': Icons.settings, 'name': '设置', 'color': Colors.grey},
{'icon': Icons.message, 'name': '消息', 'color': Colors.purple},
];
Widget build(BuildContext context) {
return GridView.builder(
padding: EdgeInsets.all(20),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 15,
mainAxisSpacing: 15,
),
itemCount: apps.length,
itemBuilder: (context, index) {
final app = apps[index];
return GestureDetector(
onTap: () {
print('打开 ${app['name']}');
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: app['color'],
borderRadius: BorderRadius.circular(15),
),
child: Icon(
app['icon'],
color: Colors.white,
size: 30,
),
),
SizedBox(height: 8),
Text(
app['name'],
style: TextStyle(fontSize: 12),
),
],
),
);
},
);
}
}
案例4:瀑布流(不同高度)
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
// 需要添加依赖: flutter_staggered_grid_view: ^0.7.0
class StaggeredGrid extends StatelessWidget {
Widget build(BuildContext context) {
return MasonryGridView.count(
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
itemCount: 20,
itemBuilder: (context, index) {
return Container(
height: (index % 3 + 1) * 100.0,
color: Colors.primaries[index % Colors.primaries.length],
child: Center(child: Text('$index')),
);
},
);
}
}
案例5:可选择的网格
class SelectableGrid extends StatefulWidget {
_SelectableGridState createState() => _SelectableGridState();
}
class _SelectableGridState extends State<SelectableGrid> {
Set<int> selectedItems = {};
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: EdgeInsets.all(10),
child: Text('已选择: ${selectedItems.length} 项'),
),
Expanded(
child: GridView.builder(
padding: EdgeInsets.all(10),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: 20,
itemBuilder: (context, index) {
final isSelected = selectedItems.contains(index);
return GestureDetector(
onTap: () {
setState(() {
if (isSelected) {
selectedItems.remove(index);
} else {
selectedItems.add(index);
}
});
},
child: Container(
decoration: BoxDecoration(
color: isSelected ? Colors.blue : Colors.grey[300],
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isSelected ? Colors.blue : Colors.transparent,
width: 3,
),
),
child: Center(
child: Text(
'$index',
style: TextStyle(
color: isSelected ? Colors.white : Colors.black,
fontSize: 20,
),
),
),
),
);
},
),
),
],
);
}
}
完整示例
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: GridViewDemo(),
);
}
}
class GridViewDemo extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('GridView 示例')),
body: GridView.builder(
padding: EdgeInsets.all(10),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: 20,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.primaries[index % Colors.primaries.length],
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'$index',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
);
},
),
);
}
}
常见问题
1. GridView 在 Column 中不显示?
// ❌ 错误
Column(
children: [
GridView.count(...), // 会报错
],
)
// ✅ 解决:使用 Expanded
Column(
children: [
Expanded(
child: GridView.count(...),
),
],
)
// ✅ 或使用 shrinkWrap
Column(
children: [
GridView.count(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
...
),
],
)
2. 如何设置不同的宽高比?
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.7, // 宽:高 = 0.7:1
),
...
)
3. 如何实现瀑布流?
使用 flutter_staggered_grid_view 包。
属性速查表
| 属性 | 说明 |
|---|---|
gridDelegate |
网格代理 |
itemCount |
项目数量 |
itemBuilder |
项目构建器 |
padding |
内边距 |
scrollDirection |
滚动方向 |
shrinkWrap |
是否根据子组件调整大小 |
SliverGridDelegateWithFixedCrossAxisCount 参数
| 参数 | 说明 |
|---|---|
crossAxisCount |
列数 |
mainAxisSpacing |
主轴间距 |
crossAxisSpacing |
交叉轴间距 |
childAspectRatio |
宽高比 |
总结
GridView 的核心要点:
- GridView.count - 固定列数
- GridView.extent - 固定最大宽度
- GridView.builder - 动态网格(推荐)
- 使用 gridDelegate 控制布局
- childAspectRatio 控制宽高比
记住:
- 大量数据用 GridView.builder
- 在 Column 中使用需要 Expanded 或 shrinkWrap
- crossAxisCount 设置列数
- childAspectRatio 设置宽高比
GridView 是展示网格内容的最佳选择!
更多推荐
所有评论(0)