Flutter贪吃蛇游戏:build方法使用旧grid变量致界面无变化
问题解决:Flutter贪吃蛇界面不更新
核心问题分析
- setState滥用且位置错误:
gameTick中循环修改蛇身坐标时,每次循环都调用setState,不仅触发多次不必要的UI重建,还可能导致状态更新不连贯。 - grid变量引用未更新:
grid作为类成员变量,直接修改列表内的Container元素时,列表本身的引用没有变化,Flutter状态检测机制会认为数据未改变,因此不更新UI。 - Timer未正确赋值:
initState中创建的Timer没有赋值给类成员timer,导致dispose时无法正确取消定时器。
修复后的代码
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:swipe/swipe.dart'; void main() { runApp( MaterialApp( title: 'snake', theme: ThemeData( brightness: Brightness.dark, primaryColor: Colors.blueGrey, ), home: Game(), ), ); } class Game extends StatefulWidget { const Game({Key? key}) : super(key: key); @override State<Game> createState() => _GameState(); } class _GameState extends State<Game> { List<List<int>> snake = [ [5, 5], [6, 5], [7, 5] ]; Timer? timer; @override void initState() { // 正确赋值timer,确保dispose时能取消 timer = Timer.periodic(Duration(seconds: 1), (Timer t) => gameTick()); super.initState(); } @override void dispose() { timer?.cancel(); super.dispose(); } void gameTick() { // 把所有蛇身修改逻辑包裹在一次setState中 setState(() { for (List<int> pair in snake) { pair[0]--; } }); } @override Widget build(BuildContext context) { // 每次build时重新创建grid列表,确保引用更新 List<Container> grid = []; for (int i = 0; i < 121; i++) { int row = (i / 11).floor(); int col = i % 11; bool isSnake = false; // 先判断当前格子是否属于蛇身 for (List<int> pair in snake) { if (pair[0] == row && pair[1] == col) { isSnake = true; break; } } grid.add(Container( decoration: BoxDecoration( color: isSnake ? Colors.greenAccent : null, border: Border.all( color: isSnake ? Color(0xFF383434) : Colors.greenAccent, width: 2, ), ), )); } return Scaffold( body: SizedBox.expand( child: Swipe( onSwipeUp: () { /* 你的滑动逻辑 */ }, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[ Expanded( child: GridView.count( physics: NeverScrollableScrollPhysics(), crossAxisCount: 11, children: grid, ), ), ], ), ), ), ); } }
关键修改点说明
- 优化setState调用:将蛇身的所有坐标修改放在一次
setState中,确保状态更新后只触发一次UI重建,避免多次重建导致的性能问题和状态不一致。 - 重建grid列表:在
build方法中每次创建新的grid列表,而非修改类成员变量的元素。这样每次状态变化时,GridView会接收到新的列表引用,从而触发UI更新。 - 修复Timer赋值:
initState中创建Timer时赋值给timer变量,保证dispose时能正确取消定时器,避免内存泄漏。 - 简化格子判断逻辑:先判断当前格子是否属于蛇身,再统一创建Container,让代码更清晰易读。
内容的提问来源于stack exchange,提问作者DR4NKR1D3R
相关产品推荐
相关产品推荐

