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

Flutter 6x6可滑动网格手势方向识别异常求助

Flutter网格滑动方向识别异常的解决方法

问题根源

当前代码使用onHorizontalDragEnd和onVerticalDragEnd的速度值判断方向,存在两个核心问题:

  • 斜向滑动时,水平和垂直方向的速度都不为零,会同时触发两个方向的回调,导致误判
  • 滑动速度较低时,速度值可能出现波动,导致方向判断反转

解决方案

改用Pan手势统一处理滑动,通过记录滑动的起始和结束位置,计算位移差来判断主滑动方向,并增加位移阈值过滤误触。

修改后的完整代码

import 'package:flutter/material.dart';
import 'dart:math';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final List<List<String>> gridData = [
    ['A', 'B', 'C', 'D', 'E', 'F'],
    ['G', 'H', 'I', 'J', 'K', 'L'],
    ['M', 'N', 'O', 'P', 'Q', 'R'],
    ['S', 'T', 'U', 'V', 'W', 'X'],
    ['Y', 'Z', 'A', 'B', 'C', 'D'],
    ['E', 'F', 'G', 'H', 'I', 'J'],
  ];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Swipe Grid Demo'),
        ),
        body: SwipeGrid(gridData),
      ),
    );
  }
}

class SwipeGrid extends StatefulWidget {
  final List<List<String>> gridData;

  const SwipeGrid(this.gridData);

  @override
  _SwipeGridState createState() => _SwipeGridState();
}

class _SwipeGridState extends State<SwipeGrid> {
  List<List<dynamic>> currentGridData = [];
  Offset? _startOffset;
  // 最小滑动阈值,过滤轻微抖动
  final double _minSwipeDistance = 15.0;

  @override
  void initState() {
    super.initState();
    currentGridData = widget.gridData.map((row) => List.from(row)).toList();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanStart: (details) {
        // 记录滑动起始位置
        _startOffset = details.globalPosition;
      },
      onPanEnd: (details) {
        if (_startOffset == null) return;

        final endOffset = details.globalPosition;
        final dx = endOffset.dx - _startOffset!.dx;
        final dy = endOffset.dy - _startOffset!.dy;

        // 判断主滑动方向
        if (dx.abs() > dy.abs()) {
          // 横向滑动
          if (dx.abs() < _minSwipeDistance) return; // 过滤小位移
          setState(() {
            if (dx > 0) {
              shiftRight();
            } else {
              shiftLeft();
            }
          });
        } else {
          // 纵向滑动
          if (dy.abs() < _minSwipeDistance) return; // 过滤小位移
          setState(() {
            if (dy > 0) {
              shiftDown();
            } else {
              shiftUp();
            }
          });
        }

        _startOffset = null;
      },
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: List.generate(
            currentGridData.length,
            (rowIndex) {
              return Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: List.generate(
                  currentGridData[rowIndex].length,
                  (colIndex) {
                    return Container(
                      width: 50,
                      height: 50,
                      margin: const EdgeInsets.all(4),
                      decoration: BoxDecoration(
                        color: Colors.blue,
                        borderRadius: BorderRadius.circular(8),
                      ),
                      child: Center(
                        child: Text(
                          currentGridData[rowIndex][colIndex],
                          style: const TextStyle(fontSize: 24, color: Colors.white),
                        ),
                      ),
                    );
                  },
                ),
              );
            },
          ),
        ),
      ),
    );
  }

  void shiftRight() {
    for (int i = 0; i < currentGridData.length; i++) {
      currentGridData[i].removeLast();
      currentGridData[i].insert(0, _generateRandomLetter());
    }
    print("swipe right");
  }

  void shiftLeft() {
    for (int i = 0; i < currentGridData.length; i++) {
      currentGridData[i].removeAt(0);
      currentGridData[i].add(_generateRandomLetter());
    }
    print("swipe left");
  }

  void shiftDown() {
    // 修正纵向滑动逻辑,每列向下移位,最后一行移出,第一行新增
    for (int col = 0; col < currentGridData[0].length; col++) {
      currentGridData.last.removeAt(col);
      currentGridData.first.insert(col, _generateRandomLetter());
      for (int row = currentGridData.length - 1; row > 0; row--) {
        currentGridData[row].insert(col, currentGridData[row-1].removeAt(col));
      }
    }
    print("swipe down");
  }

  void shiftUp() {
    // 修正纵向滑动逻辑,每列向上移位,第一行移出,最后一行新增
    for (int col = 0; col < currentGridData[0].length; col++) {
      currentGridData.first.removeAt(col);
      currentGridData.last.insert(col, _generateRandomLetter());
      for (int row = 0; row < currentGridData.length - 1; row++) {
        currentGridData[row].insert(col, currentGridData[row+1].removeAt(col));
      }
    }
    print("swipe up");
  }

  // 生成随机大写字母
  String _generateRandomLetter() {
    final random = Random();
    return String.fromCharCode(random.nextInt(26) + 65);
  }
}

关键修改说明

  1. 改用Pan手势:用onPanStart和onPanEnd替代单独的水平/垂直拖拽回调,统一处理所有滑动场景
  2. 位移判断方向:通过计算滑动的水平/垂直位移差的绝对值,判断主滑动方向,避免斜滑时的误触发
  3. 增加阈值过滤:设置_minSwipeDistance,只有位移超过该值才判定为有效滑动,避免手指抖动导致的误操作
  4. 修正纵向滑动逻辑:原代码的shiftDown和shiftUp逻辑仅操作了单列,现在修改为对所有列执行移位操作,符合需求
  5. 简化新字母生成:移除冗余的fillGap逻辑,在移位时直接生成新字母插入对应位置,逻辑更清晰

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 03:10:54