You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 11:40:00