如何制作行列数固定、单元格大小自适应的响应式GridView?
解决方案
要实现固定行列数、单元格始终为正方形、视口变化仅调整单元格大小的GridView,核心是正确计算单元格尺寸,同时让SliverGridDelegateWithFixedCrossAxisCount自动维护布局逻辑,避免手动设置高度导致的冲突。
关键思路
- 基于视口宽度计算单元格尺寸:因为横向固定5列,先算出扣除左右padding和列间距后的可用宽度,再平均分配给每一列,保证每行始终5个单元格。
- 依赖
childAspectRatio: 1确保正方形:这个参数会让单元格高度等于宽度,无需手动设置height。 - 移除手动设置的
height:避免和SliverGridDelegate自动计算的尺寸冲突。
修改后的代码
return LayoutBuilder( builder: (context, constraints) { // 固定配置参数 const crossAxisCount = 5; const crossAxisSpacing = 4.0; const paddingHorizontal = 36.0 * 2; // 左右padding总和 // 计算可用宽度:视口宽度 - 左右padding - 所有列间距 final availableWidth = constraints.maxWidth - paddingHorizontal - (crossAxisCount - 1) * crossAxisSpacing; // 单元格尺寸:可用宽度平均分配给每一列 final cellSize = availableWidth / crossAxisCount; return GridView.builder( physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAxisCount, mainAxisSpacing: 4, crossAxisSpacing: crossAxisSpacing, childAspectRatio: 1, // 强制保持正方形 ), itemBuilder: (context, index) { return Container( decoration: BoxDecoration( border: Border.all( color: Theme.of(context).primaryColorLight, ), color: Colors.white, ), child: Tile(index: index, size: cellSize), // 若Tile需要尺寸,可传递参数 ); }, padding: const EdgeInsets.fromLTRB(36, 20, 36, 20), itemCount: 30, ); }, );
代码说明
- 使用
LayoutBuilder获取父组件的约束尺寸(比直接用MediaQuery更准确,能适配父容器的限制)。 - 计算
availableWidth时必须扣除padding和列间距,否则单元格会超出视口或布局错乱。 - 移除
Container的height属性,childAspectRatio:1会让GridView自动将单元格高度设为等于宽度,完美保持正方形。 - 若自定义
Tile组件需要知道单元格尺寸,可将cellSize作为参数传入。
原代码的问题点
原代码手动设置height: cellSize,但如果cellSize计算未考虑GridView的padding和crossAxisSpacing,会导致单元格实际尺寸与Delegate计算的不一致,出现布局偏移或溢出。上述计算方式完全匹配Delegate的布局逻辑,能保证行列固定且单元格始终为正方形。
内容的提问来源于stack exchange,提问作者Al C
相关产品推荐
相关产品推荐

