mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add a [valid] property of [MouseTrackerAnnotation] indicates the annotation states. (#69866)
This commit is contained in:
parent
e803b13f01
commit
543d3cfa57
@ -55,10 +55,11 @@ class MouseTrackerAnnotation with Diagnosticable {
|
||||
this.onEnter,
|
||||
this.onExit,
|
||||
this.cursor = MouseCursor.defer,
|
||||
this.validForMouseTracker = true,
|
||||
}) : assert(cursor != null);
|
||||
|
||||
/// Triggered when a mouse pointer, with or without buttons pressed, has
|
||||
/// entered the region.
|
||||
/// entered the region and [validForMouseTracker] is true.
|
||||
///
|
||||
/// This callback is triggered when the pointer has started to be contained by
|
||||
/// the region, either due to a pointer event, or due to the movement or
|
||||
@ -72,7 +73,7 @@ class MouseTrackerAnnotation with Diagnosticable {
|
||||
final PointerEnterEventListener? onEnter;
|
||||
|
||||
/// Triggered when a mouse pointer, with or without buttons pressed, has
|
||||
/// exited the region.
|
||||
/// exited the region and [validForMouseTracker] is true.
|
||||
///
|
||||
/// This callback is triggered when the pointer has stopped being contained
|
||||
/// by the region, either due to a pointer event, or due to the movement or
|
||||
@ -100,6 +101,15 @@ class MouseTrackerAnnotation with Diagnosticable {
|
||||
/// * [MouseRegion.cursor], which provide values to this field.
|
||||
final MouseCursor cursor;
|
||||
|
||||
/// Whether this is included when [MouseTracker] collects the list of annotations.
|
||||
///
|
||||
/// If [validForMouseTracker] is false, this object is excluded from the current annotation list
|
||||
/// even if it's included in the hit test, affecting mouse-related behavior such as enter events,
|
||||
/// exit events, and mouse cursors. The [validForMouseTracker] does not affect hit testing.
|
||||
///
|
||||
/// The [validForMouseTracker] is true for [MouseTrackerAnnotation]s built by the constructor.
|
||||
final bool validForMouseTracker;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
@ -487,7 +497,7 @@ mixin _MouseTrackerEventMixin on BaseMouseTracker {
|
||||
final PointerExitEvent baseExitEvent = PointerExitEvent.fromMouseEvent(latestEvent);
|
||||
lastAnnotations.forEach((MouseTrackerAnnotation annotation, Matrix4 transform) {
|
||||
if (!nextAnnotations.containsKey(annotation))
|
||||
if (annotation.onExit != null)
|
||||
if (annotation.validForMouseTracker && annotation.onExit != null)
|
||||
annotation.onExit!(baseExitEvent.transformed(lastAnnotations[annotation]));
|
||||
});
|
||||
|
||||
@ -498,7 +508,7 @@ mixin _MouseTrackerEventMixin on BaseMouseTracker {
|
||||
).toList();
|
||||
final PointerEnterEvent baseEnterEvent = PointerEnterEvent.fromMouseEvent(latestEvent);
|
||||
for (final MouseTrackerAnnotation annotation in enteringAnnotations.reversed) {
|
||||
if (annotation.onEnter != null)
|
||||
if (annotation.validForMouseTracker && annotation.onEnter != null)
|
||||
annotation.onEnter!(baseEnterEvent.transformed(nextAnnotations[annotation]));
|
||||
}
|
||||
}
|
||||
|
||||
@ -732,6 +732,9 @@ mixin _PlatformViewGestureMixin on RenderBox implements MouseTrackerAnnotation {
|
||||
@override
|
||||
MouseCursor get cursor => MouseCursor.uncontrolled;
|
||||
|
||||
@override
|
||||
bool get validForMouseTracker => true;
|
||||
|
||||
@override
|
||||
void handleEvent(PointerEvent event, HitTestEntry entry) {
|
||||
if (event is PointerDownEvent) {
|
||||
|
||||
@ -2777,11 +2777,13 @@ class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation
|
||||
this.onHover,
|
||||
this.onExit,
|
||||
MouseCursor cursor = MouseCursor.defer,
|
||||
bool validForMouseTracker = true,
|
||||
bool opaque = true,
|
||||
RenderBox? child,
|
||||
}) : assert(opaque != null),
|
||||
assert(cursor != null),
|
||||
_cursor = cursor,
|
||||
_validForMouseTracker = validForMouseTracker,
|
||||
_opaque = opaque,
|
||||
super(child);
|
||||
|
||||
@ -2849,6 +2851,25 @@ class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool get validForMouseTracker => _validForMouseTracker;
|
||||
bool _validForMouseTracker;
|
||||
|
||||
@override
|
||||
void attach(PipelineOwner owner) {
|
||||
super.attach(owner);
|
||||
_validForMouseTracker = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void detach() {
|
||||
// It's possible that the renderObject be detached during mouse events
|
||||
// dispatching, set the [MouseTrackerAnnotation.validForMouseTracker] false to prevent
|
||||
// the callbacks from being called.
|
||||
_validForMouseTracker = false;
|
||||
super.detach();
|
||||
}
|
||||
|
||||
@override
|
||||
void performResize() {
|
||||
size = constraints.biggest;
|
||||
@ -2868,6 +2889,7 @@ class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation
|
||||
));
|
||||
properties.add(DiagnosticsProperty<MouseCursor>('cursor', cursor, defaultValue: MouseCursor.defer));
|
||||
properties.add(DiagnosticsProperty<bool>('opaque', opaque, defaultValue: true));
|
||||
properties.add(FlagProperty('validForMouseTracker', value: validForMouseTracker, defaultValue: true, ifFalse: 'invalid for MouseTracker'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -72,7 +72,7 @@ class TestMouseTrackerFlutterBinding extends BindingBase
|
||||
|
||||
// An object that mocks the behavior of a render object with [MouseTrackerAnnotation].
|
||||
class TestAnnotationTarget with Diagnosticable implements MouseTrackerAnnotation, HitTestTarget {
|
||||
const TestAnnotationTarget({this.onEnter, this.onHover, this.onExit, this.cursor = MouseCursor.defer});
|
||||
const TestAnnotationTarget({this.onEnter, this.onHover, this.onExit, this.cursor = MouseCursor.defer, this.validForMouseTracker = true});
|
||||
|
||||
@override
|
||||
final PointerEnterEventListener? onEnter;
|
||||
@ -85,6 +85,9 @@ class TestAnnotationTarget with Diagnosticable implements MouseTrackerAnnotation
|
||||
@override
|
||||
final MouseCursor cursor;
|
||||
|
||||
@override
|
||||
final bool validForMouseTracker;
|
||||
|
||||
@override
|
||||
void handleEvent(PointerEvent event, HitTestEntry entry) {
|
||||
if (event is PointerHoverEvent)
|
||||
|
||||
@ -1695,6 +1695,7 @@ void main() {
|
||||
onExit: (PointerExitEvent event) {},
|
||||
onHover: (PointerHoverEvent event) {},
|
||||
cursor: SystemMouseCursors.click,
|
||||
validForMouseTracker: false,
|
||||
child: RenderErrorBox(),
|
||||
).debugFillProperties(builder);
|
||||
|
||||
@ -1706,6 +1707,7 @@ void main() {
|
||||
'size: MISSING',
|
||||
'listeners: enter, hover, exit',
|
||||
'cursor: SystemMouseCursor(click)',
|
||||
'invalid for MouseTracker',
|
||||
]);
|
||||
});
|
||||
|
||||
@ -1728,6 +1730,37 @@ void main() {
|
||||
await gesture.moveBy(const Offset(10.0, 10.0));
|
||||
expect(tester.binding.hasScheduledFrame, isFalse);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/67044
|
||||
testWidgets('Handle mouse events should ignore the detached MouseTrackerAnnotation', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Center(
|
||||
child: Draggable<int>(
|
||||
feedback: Container(width: 20, height: 20, color: Colors.blue),
|
||||
childWhenDragging: Container(width: 20, height: 20, color: Colors.yellow),
|
||||
child: RaisedButton(child: const Text('Drag me'), onPressed: (){}),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Long press the button with mouse.
|
||||
final Offset textFieldPos = tester.getCenter(find.byType(Text));
|
||||
final TestGesture gesture = await tester.startGesture(
|
||||
textFieldPos,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
addTearDown(gesture.removePointer);
|
||||
await tester.pump(const Duration(seconds: 2));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Drag the Draggable Widget will replace the child with [childWhenDragging].
|
||||
await gesture.moveBy(const Offset(10.0, 10.0));
|
||||
await tester.pump(); // Trigger detach the button.
|
||||
|
||||
// Continue drag mouse should not trigger any assert.
|
||||
await gesture.moveBy(const Offset(10.0, 10.0));
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
}
|
||||
|
||||
// Render widget `topLeft` at the top-left corner, stacking on top of the widget
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user