Flutter多点长按手势识别插件multi_long_press_gesture_recognizer的使用

发布于 1周前 作者 caililin 来自 Flutter

Flutter多点长按手势识别插件multi_long_press_gesture_recognizer的使用

描述

multi_long_press_gesture_recognizer 是一个Flutter插件,能够识别用户在相同位置长时间按下多个指针的情况。它与Flutter自带的 LongPressGestureRecognizer 类似,但支持多点触控。

功能特性

  • 支持多点长按检测:可以识别多个指针同时长按。
  • 可配置的滑动容差:可以在手势识别前和识别后配置滑动容差。
  • 可配置的长按持续时间:可以设置触发长按事件所需的最短时间。

完整示例Demo

以下是一个完整的示例代码,展示了如何使用 multi_long_press_gesture_recognizer 插件来实现多点长按手势识别。

import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:multi_long_press_gesture_recognizer/multi_long_press_gesture_recognizer.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter MLP Demo",
      theme: ThemeData(),
      darkTheme: ThemeData.dark(),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final Queue<MapEntry<Type?, int>> _lastStates = Queue();
  int _counter = 0;

  int maxSize = 10;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  void _addState(Object? details) {
    var entry = (_lastStates.lastOrNull?.key == details?.runtimeType
            ? _lastStates.removeLast()
            : null) ??
        MapEntry(details?.runtimeType, 0);
    _lastStates.addWithLimit(MapEntry(entry.key, entry.value + 1), maxSize);
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return RawGestureDetector(
      gestures: {
        MultiLongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers<
            MultiLongPressGestureRecognizer>(
          () => MultiLongPressGestureRecognizer(
            duration: const Duration(milliseconds: 500), // 设置长按持续时间为500毫秒
            preAcceptSlopTolerance: 18.0, // 设置手势识别前的滑动容差
            pointerThreshold: 2, // 设置触发长按事件所需的最小指针数量
          ),
          (instance) {
            instance
              ..onMultiLongPressDown = (details) {
                setState(() => _addState(details)); // 处理多点长按按下事件
              }
              ..onMultiLongPress = (details) {
                HapticFeedback.vibrate(); // 触发震动反馈
                _addState(details); // 记录状态
                _incrementCounter(); // 增加计数器
              }
              ..onMultiLongPressCancel = () {
                setState(() => _addState(null)); // 处理多点长按取消事件
              }
              ..onMultiLongPressMoveUpdate = (details) {
                setState(() => _addState(details)); // 处理多点长按移动事件
              }
              ..onMultiLongPressUp = (details) {
                setState(() => _addState(details)); // 处理多点长按抬起事件
              };
          },
        ),
      },
      child: Scaffold(
        appBar: AppBar(
          title: const Text("Flutter Multi-Long-Press Demo"),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              _buildStatesLog(context), // 构建状态日志
              _buildLegend(context), // 构建图例
              Expanded(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    const Text("Last recognizer state:"),
                    Text(
                      _lastStates.lastOrNull != null
                          ? _getName(_lastStates.last.key)
                          : "-",
                      style: Theme.of(context).textTheme.headlineMedium,
                    ),
                    const Flexible(child: SizedBox(height: 50)),
                    const Text(
                      "You have triggered the gesture this many times:",
                    ),
                    Text(
                      '$_counter',
                      style: Theme.of(context).textTheme.headlineMedium,
                    ),
                  ],
                ),
              ),
              Flexible(
                child: Padding(
                  padding: const EdgeInsets.all(32.0),
                  child: DecoratedBox(
                    decoration: BoxDecoration(
                      borderRadius: const BorderRadius.all(Radius.circular(25)),
                      border: Border.all(
                        color: Theme.of(context).colorScheme.onSurface,
                        width: 5.0,
                      ),
                      color: Theme.of(context).colorScheme.surfaceBright,
                    ),
                    child: Padding(
                      padding: const EdgeInsets.all(5.0),
                      child: ClipRRect(
                        borderRadius:
                            const BorderRadius.all(Radius.circular(20)),
                        child: InteractiveViewer(
                          boundaryMargin: const EdgeInsets.all(500.0),
                          minScale: 0.5,
                          maxScale: 2.0,
                          child: Container(
                            height: 300,
                            width: 300,
                            decoration: BoxDecoration(
                              border: Border.all(
                                color: Theme.of(context).colorScheme.onSurface,
                                width: 4.0,
                              ),
                              gradient: const LinearGradient(
                                begin: Alignment.topLeft,
                                end: Alignment(0.8, 1),
                                colors: [
                                  Color(0xff1f005c),
                                  Color(0xff5b0060),
                                  Color(0xff870160),
                                  Color(0xffac255e),
                                  Color(0xffca485c),
                                  Color(0xffe16b5c),
                                  Color(0xfff39060),
                                  Color(0xffffb56b),
                                ],
                                tileMode: TileMode.mirror,
                              ),
                            ),
                            child: const Center(
                              child: Text(
                                "Drag Me",
                                style: TextStyle(
                                  fontSize: 28,
                                  color: Colors.white,
                                  fontWeight: FontWeight.w500,
                                ),
                              ),
                            ),
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildStatesLog(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: DecoratedBox(
        decoration: BoxDecoration(
          border: Border.all(
            width: 2.0,
            color: Theme.of(context).colorScheme.onSurface,
          ),
          borderRadius: const BorderRadius.all(Radius.circular(25)),
        ),
        child: Padding(
          padding: const EdgeInsets.all(4.0),
          child: LayoutBuilder(
              builder: (BuildContext context, BoxConstraints constraints) {
            const double circleDiameter = 30.0;
            const double circlePadding = 2.0;
            const double circleSize = circleDiameter + (circlePadding * 2);

            var newMaxSize = constraints.maxWidth ~/ circleSize;
            if (newMaxSize != maxSize) {
              _lastStates.clearWithLimit(newMaxSize);
            }
            maxSize = newMaxSize;

            return ConstrainedBox(
              constraints: const BoxConstraints.tightFor(height: circleSize),
              child: Row(
                children: [
                  for (MapEntry<Type?, int> state in _lastStates.take(maxSize))
                    _buildStateIcon(
                      circlePadding,
                      circleDiameter,
                      3.0,
                      state.key,
                      value: state.value == 1 ? null : state.value.toString(),
                    ),
                ],
              ),
            );
          }),
        ),
      ),
    );
  }

  Widget _buildLegend(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        _buildStateDescription(MultiLongPressDownDetails),
        _buildStateDescription(MultiLongPressDetails),
        _buildStateDescription(MultiLongPressMoveUpdateDetails),
        _buildStateDescription(MultiLongPressUpDetails),
        _buildStateDescription(null),
      ],
    );
  }

  Widget _buildStateDescription(Type? state, {TextStyle? textStyle}) {
    textStyle ??= const TextStyle(fontWeight: FontWeight.bold);
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        _buildStateIcon(0, 14.0, 2.0, state),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 4.0),
          child: Text("=", style: textStyle),
        ),
        Text(_getName(state), style: textStyle),
      ],
    );
  }

  Widget _buildStateIcon(
    double circlePadding,
    double circleDiameter,
    double borderWidth,
    Type? state, {
    String? value,
  }) {
    return Padding(
      padding: EdgeInsets.all(circlePadding),
      child: Container(
        width: circleDiameter,
        height: circleDiameter,
        decoration: BoxDecoration(
          color: _getColor(state),
          border: Border.all(
            color: _getBorderColor(state),
            width: borderWidth,
          ),
          shape: BoxShape.circle,
        ),
        child: value != null
            ? Center(
                child: Text(
                  value,
                  style: TextStyle(
                    fontSize: value.length > 2 ? 8.0 : 14.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              )
            : null,
      ),
    );
  }

  Color? _getColor(Type? state) {
    switch (state) {
      case MultiLongPressDetails:
        return Colors.green;
      case MultiLongPressDownDetails:
        return Colors.blue;
      case MultiLongPressMoveUpdateDetails:
        return null;
      case MultiLongPressUpDetails:
        return null;
      case null:
        return null;
      default:
        return null;
    }
  }

  Color _getBorderColor(Type? state) {
    switch (state) {
      case MultiLongPressDetails:
        return Colors.green;
      case MultiLongPressDownDetails:
        return Colors.blue;
      case MultiLongPressMoveUpdateDetails:
        return Colors.yellow.shade600;
      case MultiLongPressUpDetails:
        return Colors.orange;
      case null:
        return Colors.grey;
      default:
        return Colors.purple;
    }
  }

  String _getName(Type? state) {
    switch (state) {
      case MultiLongPressDetails:
        return "Press";
      case MultiLongPressDownDetails:
        return "Down";
      case MultiLongPressMoveUpdateDetails:
        return "Move";
      case MultiLongPressUpDetails:
        return "Up";
      case null:
        return "Cancel";
      default:
        throw UnsupportedError("Unknown state: $state");
    }
  }
}

extension QueueExt<T> on Queue<T> {
  void addWithLimit(T element, int limit) {
    while (length >= limit) {
      removeFirst();
    }
    add(element);
  }

  void clearWithLimit(int limit) {
    while (length >= limit) {
      removeFirst();
    }
  }
}

更多关于Flutter多点长按手势识别插件multi_long_press_gesture_recognizer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter多点长按手势识别插件multi_long_press_gesture_recognizer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用multi_long_press_gesture_recognizer插件的示例代码。这个插件允许你识别多点长按手势。首先,你需要确保已经添加了该插件到你的pubspec.yaml文件中:

dependencies:
  flutter:
    sdk: flutter
  multi_long_press_gesture_recognizer: ^最新版本号  # 请替换为实际的最新版本号

然后,运行flutter pub get来安装插件。

以下是一个简单的示例,展示了如何使用multi_long_press_gesture_recognizer来识别多点长按手势:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Multi Long Press Gesture Recognizer Demo'),
        ),
        body: Center(
          child: MultiLongPressGestureDetector(
            onMultiLongPressStart: (details) {
              // 当多点长按开始时触发
              print('Multi Long Press Started at: ${details.globalPosition}');
              // details.pointers 包含所有参与长按的指针信息
              details.pointers.forEach((pointer) {
                print('Pointer: ${pointer.device} at position: ${pointer.position}');
              });
            },
            onMultiLongPressUpdate: (details) {
              // 当多点长按更新(即手指移动)时触发
              print('Multi Long Press Updated at: ${details.globalPosition}');
              details.pointers.forEach((pointer) {
                print('Pointer: ${pointer.device} at position: ${pointer.position}');
              });
            },
            onMultiLongPressEnd: (details) {
              // 当多点长按结束时触发
              print('Multi Long Press Ended at: ${details.globalPosition}');
              details.pointers.forEach((pointer) {
                print('Pointer: ${pointer.device} at position: ${pointer.position}');
              });
            },
            child: Container(
              width: 300,
              height: 300,
              color: Colors.blue.withOpacity(0.5),
              child: Center(
                child: Text(
                  'Long Press Multiple Points Here',
                  style: TextStyle(color: Colors.white),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个MultiLongPressGestureDetector,它监听一个容器内的多点长按手势。当多点长按开始时,onMultiLongPressStart回调会被触发,并打印出所有参与长按的指针信息。同样,当多点长按更新(即手指移动)和结束时,onMultiLongPressUpdateonMultiLongPressEnd回调也会被触发,并打印出相应的信息。

这个插件为多点长按手势提供了细粒度的控制,允许你处理每个指针的位置和状态,非常适合需要复杂手势识别的应用场景。

回到顶部