如何在Flame框架中实现可选中的单元格网格?
在Flame中实现可点击高亮的网格
核心实现思路
- 基于Flame的
Game类构建逻辑,通过重写render方法绘制网格 - 混入
TapDetector获取Flame的点击事件回调,将屏幕坐标转换为网格行列索引 - 维护选中位置集合,绘制时根据状态切换单元格颜色
完整示例代码
import 'package:equatable/equatable.dart'; import 'package:flame/game.dart'; import 'package:flame/input.dart'; import 'package:flutter/material.dart'; void main() { runApp(GameWidget(game: GridGame())); } class Position extends Equatable { final int row; final int col; const Position(this.row, this.col); @override List<Object?> get props => [row, col]; } class GridGame extends Game with TapDetector { // 网格行列数 static const int gridSize = 8; // 单个单元格尺寸 static const double cellSize = 20.0; // 网格总尺寸 final double gridTotalSize = gridSize * cellSize; // 存储选中的单元格位置 final Set<Position> selectedPositions = {}; @override void render(Canvas canvas) { // 将画布原点移到屏幕中心,和原生实现对齐 final centerOffset = Offset(size.width / 2 - gridTotalSize / 2, size.height / 2 - gridTotalSize / 2); canvas.translate(centerOffset.dx, centerOffset.dy); // 遍历绘制所有单元格 for (int row = 0; row < gridSize; row++) { for (int col = 0; col < gridSize; col++) { final x = col * cellSize; final y = row * cellSize; final rect = Rect.fromLTWH(x, y, cellSize, cellSize); // 绘制单元格背景:选中为红色,否则透明 canvas.drawRect( rect, Paint() ..color = selectedPositions.contains(Position(row, col)) ? Colors.red : Colors.transparent, ); // 绘制单元格边框 canvas.drawRect( rect, Paint() ..color = Colors.black ..style = PaintingStyle.stroke ..strokeWidth = 1.0, ); } } } @override void update(double dt) { // 无额外更新逻辑,若需动画可在此处理 } @override void onTapDown(TapDownInfo info) { // 将点击坐标转换为网格局部坐标 final localPosition = info.eventPosition.game; final centerOffset = Offset(size.width / 2 - gridTotalSize / 2, size.height / 2 - gridTotalSize / 2); final gridLocalX = localPosition.dx - centerOffset.dx; final gridLocalY = localPosition.dy - centerOffset.dy; // 计算对应的行列索引 final col = (gridLocalX / cellSize).floor(); final row = (gridLocalY / cellSize).floor(); // 检查点击是否在网格范围内 if (row >= 0 && row < gridSize && col >= 0 && col < gridSize) { final position = Position(row, col); // 切换选中状态(点击选中,再点击取消) if (selectedPositions.contains(position)) { selectedPositions.remove(position); } else { selectedPositions.add(position); } // 触发重绘 markNeedsRender(); } } }
关键部分说明
- Position类:与原生实现一致,通过
Equatable确保位置对象能被集合正确判断相等性 - GridGame类:
- 混入
TapDetector以监听Flame的点击事件 render方法:先平移画布到屏幕中心,再遍历绘制每个单元格的背景与边框,根据选中状态切换颜色onTapDown方法:将屏幕点击坐标转换为网格内的局部坐标,计算对应行列后更新选中集合,最后调用markNeedsRender()触发重绘- 尺寸控制:通过
gridSize和cellSize统一管理网格尺寸,保证与原生实现视觉效果一致
- 混入
内容的提问来源于stack exchange,提问作者Dipansh Parmar
相关产品推荐
相关产品推荐

