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); } }
关键修改说明
- 改用Pan手势:用
onPanStart和onPanEnd替代单独的水平/垂直拖拽回调,统一处理所有滑动场景 - 位移判断方向:通过计算滑动的水平/垂直位移差的绝对值,判断主滑动方向,避免斜滑时的误触发
- 增加阈值过滤:设置
_minSwipeDistance,只有位移超过该值才判定为有效滑动,避免手指抖动导致的误操作 - 修正纵向滑动逻辑:原代码的
shiftDown和shiftUp逻辑仅操作了单列,现在修改为对所有列执行移位操作,符合需求 - 简化新字母生成:移除冗余的
fillGap逻辑,在移位时直接生成新字母插入对应位置,逻辑更清晰
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

