Flutter 长按手势嵌套

440 阅读1分钟

前言:需要做一个长按弹窗的功能,但是InkWell没有点击位置的坐标回调,而GestureDetector没有长按的动画效果,想全都要那最方便的就是把他们嵌套起来,但是无论谁作为子widget都会默认拦截上层的手势事件。

自定义GestureRecognizer

Flutter框架中已经定义了很多种类型的GestureRecognizer,想要使用长按效果,我们直接继承LongPressGestureRecognizer,别的动作如双击、拖动也有对应的GestureRecognizer

class CustomLongPressGestureRecognizer extends LongPressGestureRecognizer {
  @override
  void rejectGesture(int pointer) {
    // super.rejectGesture(pointer);
    // 直接接受手势,则子widget和自身都会处理手势事件
    super.acceptGesture(pointer);
  }
}

rejectGesture中接受所有的点,就不会冲突了

定义一个MultiLongPressStartGesture组件

把上面定义的CustomLongPressGestureRecognizer放在一个组件里,从外面传入要包含的子widget和长按callback,减少使用的代码量

class MultiLongPressStartGesture extends StatelessWidget {
  const MultiLongPressStartGesture({
    Key? key,
    required this.child,
    this.callback,
  }) : super(key: key);

  final Widget child;

  // 只处理长按事件,可根据需要设置其他类型callback
  final GestureLongPressStartCallback? callback;

  @override
  Widget build(BuildContext context) {
    return RawGestureDetector(
      gestures: {
        CustomLongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers<
            CustomLongPressGestureRecognizer>(
          () => CustomLongPressGestureRecognizer(),
          (recognizer) {
            // 把对应类型的callback给自定义的recognizer
            recognizer.onLongPressStart = callback;
          },
        ),
      },
      child: child,
    );
  }
}

GestureLongPressStartCallback回调包含一个LongPressStartDetails,里面有长按位置的坐标

使用

MultiLongPressStartGesture(
  child: InkWell(
    onTap: () {
      // tap event
    },
    child: childWidget(),
  ),
  callback: (details) {
    // 长按的动作
  },
),

MultiLongPressStartGesture中嵌入InkWell,在callback中就可以有点击坐标的回调了,InkWell的动画和点击事件也没有影响,可以正常使用