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都生成新矩阵。
解决方案
- 将matrix声明为类的成员变量,仅在初始化时生成一次;
- 修改
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
相关产品推荐
相关产品推荐

