Flutter实现国际象棋坐标训练:计时器与循环逻辑求助
解决方案
因为需要维护倒计时、分数、目标坐标这些可变状态,首先要把ChessBoard从StatelessWidget改成StatefulWidget,以下是完整的实现代码:
class BoardWhite extends StatelessWidget { const BoardWhite({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Chess Coordinates'), ), body: const ChessBoard(), ); } } class ChessBoard extends StatefulWidget { const ChessBoard({super.key}); @override State<ChessBoard> createState() => _ChessBoardState(); } class _ChessBoardState extends State<ChessBoard> { int remainingTime = 30; int score = 0; String targetCoordinate = ''; Timer? countdownTimer; @override void initState() { super.initState(); // 初始化时生成第一个目标坐标并启动倒计时 generateRandomCoordinate(); startCountdown(); } @override void dispose() { // 页面销毁时取消计时器,避免内存泄漏 countdownTimer?.cancel(); super.dispose(); } void startCountdown() { countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { if (remainingTime > 0) { remainingTime--; } else { timer.cancel(); } }); }); } void generateRandomCoordinate() { // 生成随机列(A-H)和随机行(1-8) final random = Random(); final column = String.fromCharCode('A'.codeUnitAt(0) + random.nextInt(8)); final row = random.nextInt(8) + 1; setState(() { targetCoordinate = '$column$row'; }); } void handleTileTap(String coordinate) { if (remainingTime <= 0) return; // 时间到了不处理点击 if (coordinate == targetCoordinate) { setState(() { score++; }); generateRandomCoordinate(); // 正确后刷新目标坐标 } // 错误点击不做计分处理 } @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 计时器和分数显示 Text( '剩余时间: $remainingTime 秒 | 得分: $score', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 15), // 目标坐标显示 Text( '请点击: $targetCoordinate', style: const TextStyle(fontSize: 24, color: Colors.blue), ), const SizedBox(height: 15), // 列标签 Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: List.generate(8, (index) { return Container( width: 30, height: 30, alignment: Alignment.center, child: Text( String.fromCharCode('A'.codeUnitAt(0) + index), style: const TextStyle(fontSize: 20), ), ); }), ), const SizedBox(height: 10), // 棋盘网格 SizedBox( width: 300.0, height: 300.0, child: GridView.builder( physics: const NeverScrollableScrollPhysics(), // 禁止滚动,避免布局问题 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 8, ), itemBuilder: (context, index) { final row = index ~/ 8; final col = index % 8; // 转换为白方视角的坐标(行是8-1) final coordinate = '${String.fromCharCode('A'.codeUnitAt(0) + col)}${8 - row}'; return GestureDetector( onTap: () => handleTileTap(coordinate), child: Container( color: (row + col) % 2 == 0 ? Colors.white : Colors.green, // 可选:给目标坐标的格子加个高亮 decoration: coordinate == targetCoordinate && remainingTime > 0 ? BoxDecoration( border: Border.all(color: Colors.red, width: 2), ) : null, ), ); }, ), ), ], ), ); } }
关键逻辑说明
- 状态管理:用
StatefulWidget维护remainingTime(剩余时间)、score(得分)、targetCoordinate(当前目标坐标)三个核心状态,通过setState()触发UI更新,这是Flutter中更新界面的标准方式,类似C++中手动刷新UI的逻辑,但Flutter会自动处理局部重绘。 - 倒计时实现:使用
Timer.periodic每秒触发一次回调,更新剩余时间,时间归零时取消计时器。注意在dispose()中销毁计时器,避免内存泄漏。 - 随机坐标生成:通过
Random类生成0-7的随机数,转换为A-H的列和1-8的行,组合成目标坐标。 - 点击验证:点击棋盘格子时,将格子坐标与目标坐标对比,正确则加分并生成新的目标坐标,错误则无操作;时间结束后不再响应点击。
- UI优化:添加了禁止棋盘滚动的设置(
NeverScrollableScrollPhysics),避免GridView滚动导致的布局偏移;可选给目标格子添加红色边框高亮,提升用户体验。
内容的提问来源于stack exchange,提问作者m6rco
相关产品推荐
相关产品推荐

