Catch errors thrown while handling pointer events (#119577)
diff --git a/packages/flutter/lib/src/gestures/binding.dart b/packages/flutter/lib/src/gestures/binding.dart
index 383badd..023b0d5 100644
--- a/packages/flutter/lib/src/gestures/binding.dart
+++ b/packages/flutter/lib/src/gestures/binding.dart
@@ -287,9 +287,18 @@
void _handlePointerDataPacket(ui.PointerDataPacket packet) {
// We convert pointer data to logical pixels so that e.g. the touch slop can be
// defined in a device-independent manner.
- _pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, window.devicePixelRatio));
- if (!locked) {
- _flushPointerEventQueue();
+ try {
+ _pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, window.devicePixelRatio));
+ if (!locked) {
+ _flushPointerEventQueue();
+ }
+ } catch (error, stack) {
+ FlutterError.reportError(FlutterErrorDetails(
+ exception: error,
+ stack: stack,
+ library: 'gestures library',
+ context: ErrorDescription('while handling a pointer data packet'),
+ ));
}
}
diff --git a/packages/flutter/test/gestures/gesture_binding_test.dart b/packages/flutter/test/gestures/gesture_binding_test.dart
index 223d0d1..328dd4b 100644
--- a/packages/flutter/test/gestures/gesture_binding_test.dart
+++ b/packages/flutter/test/gestures/gesture_binding_test.dart
@@ -35,14 +35,20 @@
return _instance!;
}
- HandleEventCallback? callback;
+ HandleEventCallback? onHandlePointerEvent;
+
+ @override
+ void handlePointerEvent(PointerEvent event) {
+ onHandlePointerEvent?.call(event);
+ super.handlePointerEvent(event);
+ }
+
+ HandleEventCallback? onHandleEvent;
@override
void handleEvent(PointerEvent event, HitTestEntry entry) {
super.handleEvent(event, entry);
- if (callback != null) {
- callback?.call(event);
- }
+ onHandleEvent?.call(event);
}
}
@@ -58,7 +64,7 @@
);
final List<PointerEvent> events = <PointerEvent>[];
- binding.callback = events.add;
+ binding.onHandleEvent = events.add;
GestureBinding.instance.platformDispatcher.onPointerDataPacket?.call(packet);
expect(events.length, 2);
@@ -76,7 +82,7 @@
);
final List<PointerEvent> events = <PointerEvent>[];
- binding.callback = events.add;
+ binding.onHandleEvent = events.add;
GestureBinding.instance.platformDispatcher.onPointerDataPacket?.call(packet);
expect(events.length, 3);
@@ -101,7 +107,7 @@
GestureBinding.instance.pointerRouter.addGlobalRoute(pointerRouterEvents.add);
final List<PointerEvent> events = <PointerEvent>[];
- binding.callback = events.add;
+ binding.onHandleEvent = events.add;
GestureBinding.instance.platformDispatcher.onPointerDataPacket?.call(packet);
expect(events.length, 3);
@@ -126,7 +132,7 @@
);
final List<PointerEvent> events = <PointerEvent>[];
- binding.callback = events.add;
+ binding.onHandleEvent = events.add;
GestureBinding.instance.platformDispatcher.onPointerDataPacket?.call(packet);
expect(events.length, 2);
@@ -143,7 +149,7 @@
);
final List<PointerEvent> events = <PointerEvent>[];
- binding.callback = (PointerEvent event) {
+ binding.onHandleEvent = (PointerEvent event) {
events.add(event);
if (event is PointerDownEvent) {
binding.cancelPointer(event.pointer);
@@ -389,7 +395,7 @@
);
final List<PointerEvent> events = <PointerEvent>[];
- binding.callback = events.add;
+ binding.onHandleEvent = events.add;
binding.platformDispatcher.onPointerDataPacket?.call(packet);
expect(events.length, 3);
@@ -397,4 +403,28 @@
expect(events[1], isA<PointerPanZoomUpdateEvent>());
expect(events[2], isA<PointerPanZoomEndEvent>());
});
+
+ test('Error handling', () {
+ const ui.PointerDataPacket packet = ui.PointerDataPacket(
+ data: <ui.PointerData>[
+ ui.PointerData(change: ui.PointerChange.down),
+ ui.PointerData(change: ui.PointerChange.up),
+ ],
+ );
+
+ final List<String> events = <String>[];
+ binding.onHandlePointerEvent = (PointerEvent event) { throw Exception('zipzapzooey $event'); };
+ FlutterError.onError = (FlutterErrorDetails details) { events.add(details.toString()); };
+ try {
+ GestureBinding.instance.platformDispatcher.onPointerDataPacket?.call(packet);
+ expect(events.length, 1);
+ expect(events[0], contains('while handling a pointer data\npacket')); // The default stringifying behavior uses 65 character wrapWidth.
+ expect(events[0], contains('zipzapzooey'));
+ expect(events[0], contains('PointerDownEvent'));
+ expect(events[0], isNot(contains('PointerUpEvent'))); // Failure happens on the first message, remaining messages aren't processed.
+ } finally {
+ binding.onHandlePointerEvent = null;
+ FlutterError.onError = FlutterError.presentError;
+ }
+ });
}