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

如何在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();
    }
  }
}

关键部分说明

  1. Position类:与原生实现一致,通过Equatable确保位置对象能被集合正确判断相等性
  2. GridGame类:
    • 混入TapDetector以监听Flame的点击事件
    • render方法:先平移画布到屏幕中心,再遍历绘制每个单元格的背景与边框,根据选中状态切换颜色
    • onTapDown方法:将屏幕点击坐标转换为网格内的局部坐标,计算对应行列后更新选中集合,最后调用markNeedsRender()触发重绘
    • 尺寸控制:通过gridSize和cellSize统一管理网格尺寸,保证与原生实现视觉效果一致

内容的提问来源于stack exchange,提问作者Dipansh Parmar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 15:54:58