我想将TapGestureRecognizer和LongPressGestureRecognizer添加到TextSpan中。现在我可以添加任何一个,但两者都不能加。
我查看了GestureDetector类,并希望用它包装TextSpan,但是RichText元素只接受TextSpans,而不接受小部件。
这就是我现在拥有的:
TextSpan(
text: "some text",
recognizer: TapGestureRecognizer()
..onTap = () { print('tapped'); }
)
我想在代码中添加..onLongPress
。
最后,这两种手势都应该适用于单个文本跨度。
发布于 2019-10-11 14:46:17
将多个GestureRecognizer添加到TextSpan中似乎是不可能的,但是这里有一个解决方案,只使用TapGestureRecognizer,使用onTapUp和onTapDown检测抽头,并使用计时器模拟长抽头:
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,
),
],
),
),
),
);
}
发布于 2020-11-30 10:58:41
可以使用WidgetSpan设置span,并通过GestureDetector检测TapGestureRecognizer和LongPressGestureRecognizer。
TextSpan(
children: <InlineSpan>[
TextSpan(text: 'Flutter is'),
WidgetSpan(
child: GestureDetector(
onTap: () {
},
onLongPress: () {
},
child: Text(' Hello World! '),
)
),
TextSpan(text: 'the best!'),
],
)
发布于 2022-02-05 14:20:59
我将TapGestureRecognizer
和LongPressGestureRecognizer
组合成一个简单的TapAndLongPressGestureRecognizer
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';
}
https://stackoverflow.com/questions/58342982
复制相似问题