Fix MouseTracker annotation leak (#28990)
* Fix MouseTracker annotation leak
Map's remove method is typed `remove(Object key)`, which can cause bugs.
Modified the existing test to check if the annotation has been removed
from MouseTracker.
diff --git a/packages/flutter/lib/src/gestures/mouse_tracking.dart b/packages/flutter/lib/src/gestures/mouse_tracking.dart
index 5c93d4f..90a28b5 100644
--- a/packages/flutter/lib/src/gestures/mouse_tracking.dart
+++ b/packages/flutter/lib/src/gestures/mouse_tracking.dart
@@ -124,7 +124,7 @@
for (int deviceId in trackedAnnotation.activeDevices) {
annotation.onExit(PointerExitEvent.fromHoverEvent(_lastMouseEvent[deviceId]));
}
- _trackedAnnotations.remove(trackedAnnotation);
+ _trackedAnnotations.remove(annotation);
}
void _scheduleMousePositionCheck() {
@@ -171,6 +171,16 @@
return trackedAnnotation;
}
+ /// Checks if the given [MouseTrackerAnnotation] is attached to this
+ /// [MouseTracker].
+ ///
+ /// This function is only public to allow for proper testing of the
+ /// MouseTracker. Do not call in other contexts.
+ @visibleForTesting
+ bool isAnnotationAttached(MouseTrackerAnnotation annotation) {
+ return _trackedAnnotations[annotation] != null;
+ }
+
/// Tells interested objects that a mouse has entered, exited, or moved, given
/// a callback to fetch the [MouseTrackerAnnotation] associated with a global
/// offset.
diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart
index f5bb6d0..eb69561 100644
--- a/packages/flutter/lib/src/rendering/proxy_box.dart
+++ b/packages/flutter/lib/src/rendering/proxy_box.dart
@@ -2591,6 +2591,13 @@
// Object used for annotation of the layer used for hover hit detection.
MouseTrackerAnnotation _hoverAnnotation;
+ /// Object used for annotation of the layer used for hover hit detection.
+ ///
+ /// This is only public to allow for testing of Listener widgets. Do not call
+ /// in other contexts.
+ @visibleForTesting
+ MouseTrackerAnnotation get hoverAnnotation => _hoverAnnotation;
+
void _updateAnnotations() {
if (_hoverAnnotation != null && attached) {
RendererBinding.instance.mouseTracker.detachAnnotation(_hoverAnnotation);
diff --git a/packages/flutter/test/widgets/listener_test.dart b/packages/flutter/test/widgets/listener_test.dart
index 1cde7a9..54f54e8 100644
--- a/packages/flutter/test/widgets/listener_test.dart
+++ b/packages/flutter/test/widgets/listener_test.dart
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/gestures.dart';
@@ -109,6 +110,7 @@
onPointerExit: (PointerExitEvent details) => exit = details,
),
));
+ final RenderPointerListener renderListener = tester.renderObject(find.byType(Listener));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.moveTo(const Offset(400.0, 300.0));
await tester.pump();
@@ -125,6 +127,7 @@
));
expect(exit, isNotNull);
expect(exit.position, equals(const Offset(400.0, 300.0)));
+ expect(tester.binding.mouseTracker.isAnnotationAttached(renderListener.hoverAnnotation), isFalse);
});
});
}