Flutter中如何在GridView内动态调整提示Tile的尺寸?
Nonogram游戏提示Tile布局实现方案
问题现状
开发Nonogram游戏时,已完成可点击切换状态的游戏网格,但用于显示行列提示的蓝色Tile无法根据提示内容垂直(列提示)或水平(行提示)自适应扩展,且难以保证和游戏网格的行列对齐。原方案使用单一GridView无法满足需求,尝试Expanded和动态宽高比也未达到预期效果。
解决方案
放弃单一GridView的布局方式,改用分区组合布局,将界面拆分为左上角空白区、顶部行提示区、左侧列提示区、中间游戏网格区四个部分,分别控制每个区域的布局逻辑,确保提示Tile能自适应内容并和网格对齐:
1. 整体布局结构
使用外层Row包裹左侧列提示区和中间游戏区,中间游戏区再用Column包裹顶部行提示区和网格区域,左上角空白区用于对齐行列提示的交叉点。
2. 提示Tile实现
- 行提示:用
Container作为蓝色背景,内部嵌套Row排列提示数字,宽度根据提示数量动态计算,确保和对应行的网格宽度匹配。 - 列提示:用
Container作为蓝色背景,内部嵌套Column垂直排列提示数字,高度根据提示数量动态计算,确保和对应列的网格高度匹配。
3. 修改后的完整代码
import 'package:flutter/material.dart'; class MyHomePage2 extends StatefulWidget { final int difficulty; const MyHomePage2({super.key, required this.difficulty}); @override State<MyHomePage2> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage2> { late List<List<int>> _gridState; // 二维数组存储网格状态:0=空白,1=填充,2=标记 bool enable = true; // 模拟提示数据,实际可根据难度生成 final List<List<int>> rowHints = [ [3], [1, 1], [3], [1, 1], [3] ]; final List<List<int>> columnHints = [ [3], [1, 1], [3], [1, 1], [3] ]; final MaterialStateProperty<Icon?> thumbIcon = MaterialStateProperty.resolveWith<Icon?>( (Set<MaterialState> states) { if (states.contains(MaterialState.selected)) { return null; } return const Icon(Icons.close); }, ); @override void initState() { super.initState(); // 初始化网格状态,难度对应网格行列数 int gridSize = (widget.difficulty / 5).round(); _gridState = List.generate(gridSize, (index) => List.filled(gridSize, 0)); } @override Widget build(BuildContext context) { int gridSize = _gridState.length; double tileSize = MediaQuery.of(context).size.width * 0.15; // 单个网格块统一尺寸 return Scaffold( appBar: AppBar( title: const Text('Nonogram'), backgroundColor: Colors.blue, ), body: Column( children: [ Expanded( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 左侧列提示区 Column( children: List.generate(gridSize, (colIndex) { return Container( width: tileSize * columnHints[colIndex].length, height: tileSize, margin: const EdgeInsets.all(1), decoration: BoxDecoration( color: Colors.blue[100], borderRadius: BorderRadius.circular(5), // 替换为指定背景图 // image: const DecorationImage( // image: AssetImage('assets/blue_tile.png'), // fit: BoxFit.cover, // ), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: columnHints[colIndex] .map((hint) => Text( hint.toString(), style: const TextStyle(fontSize: 18), )) .toList(), ), ); }), ), // 中间游戏区(含顶部行提示) Column( children: [ // 顶部行提示区 Row( children: List.generate(gridSize, (rowIndex) { return Container( width: tileSize, height: tileSize * rowHints[rowIndex].length, margin: const EdgeInsets.all(1), decoration: BoxDecoration( color: Colors.blue[100], borderRadius: BorderRadius.circular(5), // 替换为指定背景图 // image: const DecorationImage( // image: AssetImage('assets/blue_tile.png'), // fit: BoxFit.cover, // ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: rowHints[rowIndex] .map((hint) => Text( hint.toString(), style: const TextStyle(fontSize: 18), )) .toList(), ), ); }), ), // 游戏网格区 GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: gridSize, childAspectRatio: 1, crossAxisSpacing: 1, mainAxisSpacing: 1, ), itemCount: gridSize * gridSize, itemBuilder: (context, index) { int row = index ~/ gridSize; int col = index % gridSize; int state = _gridState[row][col]; return GestureDetector( onTap: () { setState(() { if (enable) { _gridState[row][col] = state == 0 || state == 2 ? 1 : 0; } else { _gridState[row][col] = state == 2 ? 0 : 2; } }); }, child: Container( color: state == 0 ? Colors.white : state == 1 ? Colors.blueGrey[800] : Colors.transparent, child: state == 2 ? const Icon(Icons.close_rounded, size: 30) : null, decoration: BoxDecoration( border: Border.all(color: Colors.black, width: 1), ), ), ); }, ), ], ), ], ), ), // 模式切换开关 Padding( padding: const EdgeInsets.all(16.0), child: Transform.scale( scale: 2.0, child: Switch( activeColor: Colors.black, thumbIcon: thumbIcon, value: enable, onChanged: (bool value) { setState(() { enable = value; }); }, ), ), ), ], ), ); } }
关键改进点
- 使用**二维数组
_gridState**管理网格状态,比一维数组更直观,方便和行列提示对应。 - 拆分布局为独立的提示区和网格区,各自根据提示数量动态调整尺寸,确保行列严格对齐。
- 提示Tile的背景可直接替换为指定图片(注释部分已给出实现方式)。
- 网格块大小通过
tileSize统一控制,保证提示区和网格区的尺寸匹配。
内容的提问来源于stack exchange,提问作者andrea99r
相关产品推荐
相关产品推荐

