首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何向TextSpan中添加多个手势识别器?

如何向TextSpan中添加多个手势识别器?
EN

Stack Overflow用户
提问于 2019-10-11 14:05:38
回答 3查看 1.5K关注 0票数 4

我想将TapGestureRecognizer和LongPressGestureRecognizer添加到TextSpan中。现在我可以添加任何一个,但两者都不能加。

我查看了GestureDetector类,并希望用它包装TextSpan,但是RichText元素只接受TextSpans,而不接受小部件。

这就是我现在拥有的:

代码语言:javascript
运行
复制
TextSpan(
    text: "some text",
    recognizer: TapGestureRecognizer()
    ..onTap = () { print('tapped'); }
)

我想在代码中添加..onLongPress

最后,这两种手势都应该适用于单个文本跨度。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-10-11 14:46:17

将多个GestureRecognizer添加到TextSpan中似乎是不可能的,但是这里有一个解决方案,只使用TapGestureRecognizer,使用onTapUp和onTapDown检测抽头,并使用计时器模拟长抽头:

代码语言:javascript
运行
复制
TapGestureRecognizer _tapGestureRecognizer;
Timer _timer;

@override
void initState() {
  super.initState();
  _initRecognizer();
}

_initRecognizer() {
  _tapGestureRecognizer = TapGestureRecognizer();
  _tapGestureRecognizer.onTapUp = (_) {
    if (_timer != null && _timer.isActive) {
      print('Tap');
      _timer.cancel();
    }
  };
  _tapGestureRecognizer.onTapDown = (_) {
    _timer = Timer(Duration(seconds: 1), () {
      print('Long Tap');
    });
  };
}

@override
void dispose() {
  if (_timer != null) {
    _timer.cancel();
    _timer = null;
  }
  super.dispose();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    backgroundColor: Colors.grey,
    appBar: AppBar(),
    body: Padding(
      padding: EdgeInsets.all(16.0),
      child: RichText(
        text: TextSpan(
          children: [
            TextSpan(
              text: "This is some text.",
              recognizer: _tapGestureRecognizer,
              style: Theme.of(context).textTheme.title,
            ),
            TextSpan(
              text:
              "Another piece of text. Another piece of text. Another piece of text. Another piece of text.",
              style: Theme.of(context).textTheme.title,
            ),
          ],
        ),
      ),
    ),
  );
}
票数 5
EN

Stack Overflow用户

发布于 2020-11-30 10:58:41

可以使用WidgetSpan设置span,并通过GestureDetector检测TapGestureRecognizer和LongPressGestureRecognizer。

代码语言:javascript
运行
复制
 TextSpan(
          children: <InlineSpan>[
            TextSpan(text: 'Flutter is'),
            WidgetSpan(
                child: GestureDetector(
                  onTap: () {
                    
                  },
                  onLongPress: () {
                    
                  },
                  child: Text(' Hello World! '),
                )
            ),
            TextSpan(text: 'the best!'),
          ],
        )
票数 5
EN

Stack Overflow用户

发布于 2022-02-05 14:20:59

我将TapGestureRecognizerLongPressGestureRecognizer组合成一个简单的TapAndLongPressGestureRecognizer

代码语言:javascript
运行
复制
import 'package:flutter/gestures.dart';

///
/// A simple [GestureRecognizer] that combines [TapGestureRecognizer] and [LongPressGestureRecognizer]
/// It only supports two simple callbacks [onTap] and [onLongPress]
///
class TapAndLongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
  /// Creates a gesture recognizer.
  TapAndLongPressGestureRecognizer({
    Duration? duration,
    double? postAcceptSlopTolerance,
    @Deprecated(
      'Migrate to supportedDevices. '
      'This feature was deprecated after v2.3.0-1.0.pre.',
    )
        PointerDeviceKind? kind,
    Set<PointerDeviceKind>? supportedDevices,
    Object? debugOwner,
  }) : super(
          deadline: duration ?? kLongPressTimeout,
          postAcceptSlopTolerance: postAcceptSlopTolerance,
          kind: kind,
          supportedDevices: supportedDevices,
          debugOwner: debugOwner,
        );

  bool _longPressAccepted = false;
  // The buttons sent by `PointerDownEvent`. If a `PointerMoveEvent` comes with a
  // different set of buttons, the gesture is canceled.
  int? _initialButtons;

  bool _sentTapDown = false;
  bool _wonArenaForPrimaryPointer = false;

  PointerDownEvent? _down;
  PointerUpEvent? _up;

  /// Called when a long press gesture by a primary button has been recognized.
  ///
  GestureLongPressCallback? onLongPress;

  /// A pointer has stopped contacting the screen, which is recognized as a tap
  /// of a primary button.
  ///
  /// This triggers on the up event, if the recognizer wins the arena with it
  /// or has previously won, immediately following [onTapUp].
  ///
  GestureTapCallback? onTap;

  VelocityTracker? _velocityTracker;

  @override
  bool isPointerAllowed(PointerDownEvent event) {
    switch (event.buttons) {
      case kPrimaryButton:
        if (onLongPress == null) {
          return false;
        }
        break;
      default:
        return false;
    }
    return super.isPointerAllowed(event);
  }

  @override
  void addAllowedPointer(PointerDownEvent event) {
    assert(event != null);
    if (state == GestureRecognizerState.ready) {
      // If there is no result in the previous gesture arena,
      // we ignore them and prepare to accept a new pointer.
      if (_down != null && _up != null) {
        assert(_down!.pointer == _up!.pointer);
        _resetTap();
      }

      assert(_down == null && _up == null);
      // `_down` must be assigned in this method instead of `handlePrimaryPointer`,
      // because `acceptGesture` might be called before `handlePrimaryPointer`,
      // which relies on `_down` to call `handleTapDown`.
      _down = event;
    }
    if (_down != null) {
      // This happens when this tap gesture has been rejected while the pointer
      // is down (i.e. due to movement), when another allowed pointer is added,
      // in which case all pointers are simply ignored. The `_down` being null
      // means that _reset() has been called, since it is always set at the
      // first allowed down event and will not be cleared except for reset(),
      super.addAllowedPointer(event);
    }
  }

  @override
  void didExceedDeadline() {
    // Exceeding the deadline puts the gesture in the accepted state.
    resolve(GestureDisposition.accepted);
    _longPressAccepted = true;
    super.acceptGesture(primaryPointer!);
    _checkLongPressStart();
  }

  @override
  void handlePrimaryPointer(PointerEvent event) {
    if (!event.synthesized) {
      if (event is PointerDownEvent) {
        _velocityTracker = VelocityTracker.withKind(event.kind);
        _velocityTracker!.addPosition(event.timeStamp, event.localPosition);
      }
      if (event is PointerMoveEvent) {
        assert(_velocityTracker != null);
        _velocityTracker!.addPosition(event.timeStamp, event.localPosition);
      }
    }

    if (event is PointerUpEvent) {
      if (_longPressAccepted == true) {
        _checkLongPressEnd(event);
        _resetTap();
      } else {
        _up = event;
        _checkTapUp();
      }
      _resetLongPress();
    } else if (event is PointerCancelEvent) {
      _resetLongPress();
      if (_sentTapDown) {
        _checkTapCancel(event, '');
      }
      _resetTap();
    } else if (event is PointerDownEvent) {
      _initialButtons = event.buttons;
      _down = event;
      _checkTapDown();
    } else if (event is PointerMoveEvent) {
      if (event.buttons != _initialButtons) {
        resolve(GestureDisposition.rejected);
        stopTrackingPointer(primaryPointer!);
      }
    }
  }

  void _checkTapUp() {
    if (!_wonArenaForPrimaryPointer || _up == null) {
      return;
    }
    assert(_up!.pointer == _down!.pointer);
    switch (_down!.buttons) {
      case kPrimaryButton:
        if (onTap != null) {
          invokeCallback<void>('onTap', onTap!);
        }
        break;
      default:
    }
    _resetTap();
  }

  void _checkTapDown() {
    if (_sentTapDown) {
      return;
    }
    _sentTapDown = true;
  }

  void _checkTapCancel(PointerCancelEvent? event, String note) {}

  void _resetTap() {
    _sentTapDown = false;
    _wonArenaForPrimaryPointer = false;
    _up = null;
    _down = null;
  }

  void _checkLongPressStart() {
    switch (_initialButtons) {
      case kPrimaryButton:
        if (onLongPress != null) {
          invokeCallback<void>('onLongPress', onLongPress!);
        }
        break;
      default:
        assert(false, 'Unhandled button $_initialButtons');
    }
  }

  void _checkLongPressEnd(PointerEvent event) {
    _velocityTracker = null;
  }

  void _resetLongPress() {
    _longPressAccepted = false;
    _initialButtons = null;
    _velocityTracker = null;
  }

  @override
  void resolve(GestureDisposition disposition) {
    if (disposition == GestureDisposition.rejected) {
      if (_longPressAccepted) {
        // This can happen if the gesture has been canceled. For example when
        // the buttons have changed.
        _resetLongPress();
      }

      if (_wonArenaForPrimaryPointer && _sentTapDown) {
        // This can happen if the gesture has been canceled. For example, when
        // the pointer has exceeded the touch slop, the buttons have been changed,
        // or if the recognizer is disposed.
        _checkTapCancel(null, 'spontaneous');
        _resetTap();
      }
    }
    super.resolve(disposition);
  }

  @override
  void acceptGesture(int pointer) {
    if (pointer == primaryPointer && _down != null) {
      _checkTapDown();
      _wonArenaForPrimaryPointer = true;
      _checkTapUp();
    }
  }

  @override
  void rejectGesture(int pointer) {
    super.rejectGesture(pointer);
    if (pointer == primaryPointer) {
      // Another gesture won the arena.
      assert(state != GestureRecognizerState.possible);
      if (_sentTapDown) {
        _checkTapCancel(null, 'forced');
      }
      _resetTap();
    }
  }

  @override
  String get debugDescription => 'tap or long press';
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58342982

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档