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

Flutter:如何在平面内对角旋转Rect以实现单词搜索应用的斜向标记绘制

单词搜索应用对角线标记绘制优化方案

问题核心

你当前实现的单词搜索应用已完成横、竖向单词选中、校验、标记绘制全流程逻辑,斜向标记失败的核心原因是:

  1. 硬编码RotationTransition的旋转角度,没有根据斜向类型动态调整
  2. 直接使用Rect.expandToInclude生成的外接矩形作为标记范围,斜向下的外接矩形会覆盖非选中单元格,不符合显示要求
  3. 未将选中方向同步到标记组件,无法针对性调整标记样式

优化步骤

  • 在WordMarker类中新增direction字段,存储当前标记对应的选中方向,可选值:horizontal/vertical/mainDiagonal(左上到右下对角线)/counterDiagonal(右上到左下对角线)
  • 取消RotationTransition的固定角度配置,根据标记的direction字段动态计算旋转角度
  • 调整标记宽高计算逻辑,斜向标记不再使用外接矩形的宽高,根据选中单元格数量、单个单元格尺寸计算适配的宽高
  • 在拖拽开始后首次触发onPanUpdate时确定选中方向,同步到当前标记

核心修改代码示例

标记组件重构

Widget buildWordMarker(WordMarker marker) {
  // 动态计算旋转角度
  double turn = 0;
  if(marker.direction == 'mainDiagonal') turn = 45 / 360;
  if(marker.direction == 'counterDiagonal') turn = -45 / 360;

  int selectedCellCount = selection.length;
  // 动态计算标记宽高
  double markerWidth;
  double markerHeight;
  if(marker.direction.contains('Diagonal')){
    // 斜向宽度为单元格数 * 单元格宽度 * 根号2,高度自定义标记粗细即可
    markerWidth = selectedCellCount * cellWidth * 1.414;
    markerHeight = 8;
  }else{
    markerWidth = marker.rect!.width;
    markerHeight = marker.rect!.height;
  }

  return Positioned(
    left: marker.rect!.left,
    top: marker.rect!.top,
    child: RotationTransition(
      alignment: Alignment.topLeft, // 旋转锚点改到左上角,和起始单元格位置对齐
      turns: AlwaysStoppedAnimation(turn),
      child: Container(
        width: markerWidth,
        height: markerHeight,
        decoration: BoxDecoration(
          color: marker.colorSelection!.withOpacity(0.4),
          borderRadius: BorderRadius.circular(4),
        ),
      ),
    ),
  );
}

onPanUpdate新增方向初始化逻辑

void onPanUpdate(DragUpdateDetails details) {
  final currentIndex = computeLetterIndex(details.localPosition);
  if(currentIndex < 0 || currentIndex > 99) return;

  final key = uniqueLetters[currentIndex]['key'];
  final RenderBox renderBox = key.currentContext.findRenderObject();
  markerRect = renderBox.localToGlobal(Offset.zero, ancestor: context.findRenderObject()) & renderBox.size;

  // 新增:首次偏移时确定选中方向
  if(direction.isEmpty) {
    if(checkSameRow(startIndex, currentIndex)) direction = "horizontal";
    else if(checkSameCol(startIndex, currentIndex)) direction = "vertical";
    else if(checkSameMainDiagonal(startIndex, currentIndex)) direction = "mainDiagonal";
    else if(checkSameCounterDiagonal(startIndex, currentIndex)) direction = "counterDiagonal";
    // 方向同步到当前标记
    markers.last = markers.last.copyWith(direction: direction);
  }

  List<int> _selection = [];
  // 原有方向校验、选中范围生成逻辑保持不变
  if (checkSameRow(startIndex, currentIndex) && direction == "horizontal") {
    markers.last = adjustedMarker(markers.last, markerRect!);
    _selection = genSelection(startIndex, currentIndex, 1);
  } else if (checkSameCol(startIndex, currentIndex) && direction == "vertical") {
    markers.last = adjustedMarker(markers.last, markerRect!);
    _selection = genSelection(startIndex, currentIndex, nCols);
  } else if (checkSameMainDiagonal(startIndex, currentIndex) && direction == "mainDiagonal") {
    markers.last = adjustedMarker(markers.last, markerRect!);
    _selection = genSelection(startIndex, currentIndex, nCols + 1);
  } else if (checkSameCounterDiagonal(startIndex, currentIndex) && direction == "counterDiagonal") {
    markers.last = adjustedMarker(markers.last, markerRect!);
    _selection = genSelection(startIndex, currentIndex, nCols - 1);
  }

  setState(() {
    selection = _selection;
  });
}

效果说明

优化后可完美适配四个方向的选中标记绘制,标记仅覆盖选中的对应方向单元格,不会出现多余范围覆盖问题,原有单词校验逻辑无需改动。

内容的提问来源于stack exchange,提问作者Lucas santos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 11:39:04