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

Flutter中onTap触发后像素矩阵重复随机化问题求助

问题:点击像素时矩阵重新随机生成,无法单独设置为透明

我实现了一个功能:将图片隐藏在像素矩阵下方,点击任意彩色像素时,该像素应变为透明,点击所有像素后即可完全显示下方图片。但点击特定彩色元素时,目标像素未变为透明,反而所有像素重新随机生成,getAlmostRandomMatrix(size)似乎在点击时被再次调用。我曾尝试将matrix设为类变量,但并未解决问题。

原实现代码:

getAlmostRandomMatrix(size) {
    makeMatrix() => Iterable<List<Color>>.generate(
        size, (i) => List<Color>.filled(size, Colors.transparent)).toList();
    List<List<Color>> matrix = makeMatrix(); //size x size matrix with transparent colors as default
    int frequency = (size * size / 3)
        .toInt(); // this assumes that the size is a multiple of 3
    // else it will cause unequal distribution in the matrix
    List<int> distribution = [
      frequency,
      frequency,
      frequency
    ]; //this assumes that team size is 3

    Random random = Random();
    for (var i = 0; i < size; i++) {
      for (var j = 0; j < size; j++) {
        int rand = random.nextInt(3);
        if (distribution[rand] > 0) {
          switch (rand) {
            case 0:
              matrix[i][j] = Theme.of(context).colorScheme.secondary;
              break;
            case 1:
              matrix[i][j] = Theme.of(context).colorScheme.primary;
              break;
            case 2:
              matrix[i][j] = Theme.of(context).colorScheme.error;
              break;
            default:
              matrix[i][j] = Colors.transparent;
          }
          // matrix[i][j] = rand;
          distribution[rand] -= 1;
        } else {
          // brute force assignment to force equal frequency distribution
          // these colors won't be random anymore
          if (distribution[0] > 0) {
            matrix[i][j] = Theme.of(context).colorScheme.secondary;
            distribution[0] -= 1;
          } else if (distribution[1] > 0) {
            matrix[i][j] = Theme.of(context).colorScheme.primary;
            distribution[1] -= 1;
          } else {
            matrix[i][j] = Theme.of(context).colorScheme.error;
            distribution[2] -= 1;
          }
        }
      }
    }
    return matrix;
  }

  Widget paintPixelsOnReward() {
    int size = 9;
    List<List<Color>> matrix = getAlmostRandomMatrix(size);
    return Container(
        padding: const EdgeInsets.all(8.0),
        child: Stack(children: <Widget>[
          const Image(
            width: 360,
            height: 315,
            image: AssetImage('lib/assets/images/orange_butterfly_image.png'),
          ),
          Column(children: [
            for (int i = 0; i < size; i++)
              Row(
                children: [
                  for (int j = 0; j < size; j++) ...[
                    LayoutBuilder(
                      builder: (context, constraints) {
                        return Align(
                          alignment: Alignment.topLeft,
                          child: GestureDetector(
                              onTap: () {
                                // print(matrix);
                                setState(() {
                                  matrix[i][j] = Colors.transparent;
                                });
                              },
                              child: Container(
                                width: 40,
                                height: 35,
                                decoration: BoxDecoration(
                                    color: matrix[i][j],
                                    border: Border.all(
                                        width: 1, color: Colors.white)),
                              )),
                        );
                      },
                    ),
                  ]
                ],
              )
          ]),
        ]));
  }

问题原因

核心问题在于:

  • paintPixelsOnReward方法每次执行时都会调用getAlmostRandomMatrix生成全新的矩阵;
  • 点击调用setState后,Widget会重新构建,paintPixelsOnReward会再次执行,导致矩阵被重新生成,覆盖了之前修改的状态;
  • 若之前仅将matrix设为类变量,但没有调整初始化时机(比如仍在paintPixelsOnReward里重新赋值),依然会导致每次build都生成新矩阵。

解决方案

  1. 将matrix声明为类的成员变量,仅在初始化时生成一次;
  2. 修改paintPixelsOnReward,直接使用类成员变量的matrix,不再每次调用getAlmostRandomMatrix。

修改后的完整代码示例:

// 在类中声明成员变量
late List<List<Color>> _matrix;
final int _size = 9;

@override
void initState() {
  super.initState();
  // 仅在初始化时生成一次矩阵
  _matrix = getAlmostRandomMatrix(_size);
}

getAlmostRandomMatrix(size) {
  makeMatrix() => Iterable<List<Color>>.generate(
      size, (i) => List<Color>.filled(size, Colors.transparent)).toList();
  List<List<Color>> matrix = makeMatrix();
  int frequency = (size * size / 3).toInt();
  List<int> distribution = [frequency, frequency, frequency];

  Random random = Random();
  for (var i = 0; i < size; i++) {
    for (var j = 0; j < size; j++) {
      int rand = random.nextInt(3);
      if (distribution[rand] > 0) {
        switch (rand) {
          case 0:
            matrix[i][j] = Theme.of(context).colorScheme.secondary;
            break;
          case 1:
            matrix[i][j] = Theme.of(context).colorScheme.primary;
            break;
          case 2:
            matrix[i][j] = Theme.of(context).colorScheme.error;
            break;
          default:
            matrix[i][j] = Colors.transparent;
        }
        distribution[rand] -= 1;
      } else {
        if (distribution[0] > 0) {
          matrix[i][j] = Theme.of(context).colorScheme.secondary;
          distribution[0] -= 1;
        } else if (distribution[1] > 0) {
          matrix[i][j] = Theme.of(context).colorScheme.primary;
          distribution[1] -= 1;
        } else {
          matrix[i][j] = Theme.of(context).colorScheme.error;
          distribution[2] -= 1;
        }
      }
    }
  }
  return matrix;
}

Widget paintPixelsOnReward() {
  return Container(
      padding: const EdgeInsets.all(8.0),
      child: Stack(children: <Widget>[
        const Image(
          width: 360,
          height: 315,
          image: AssetImage('lib/assets/images/orange_butterfly_image.png'),
        ),
        Column(children: [
          for (int i = 0; i < _size; i++)
            Row(
              children: [
                for (int j = 0; j < _size; j++) ...[
                  LayoutBuilder(
                    builder: (context, constraints) {
                      return Align(
                        alignment: Alignment.topLeft,
                        child: GestureDetector(
                            onTap: () {
                              setState(() {
                                _matrix[i][j] = Colors.transparent;
                              });
                            },
                            child: Container(
                              width: 40,
                              height: 35,
                              decoration: BoxDecoration(
                                  color: _matrix[i][j],
                                  border: Border.all(
                                      width: 1, color: Colors.white)),
                            )),
                      );
                    },
                  ),
                ]
              ],
            )
        ]),
      ]));
}

关键改动说明

  • 将matrix和size都设为类成员变量,避免每次build重新生成;
  • 在initState中初始化_matrix,确保只生成一次;
  • paintPixelsOnReward中直接使用_matrix和_size,不再调用getAlmostRandomMatrix;
  • 点击事件中修改的是类成员变量_matrix,setState后Widget重新build时会使用更新后的矩阵状态。

内容的提问来源于stack exchange,提问作者Vipin Verma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 19:39:24