Skip to content

Commit 95da3aa

Browse files
kazbeksultanovAbdeMohlbi
authored andcommitted
Enhance error handling of WidgetsBindingObserver callbacks (flutter#181174)
<!-- Thanks for filing a pull request! Reviewers are typically assigned within a week of filing a request. To learn more about code review, see our documentation on Tree Hygiene: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md --> Wraps all 16 WidgetsBindingObserver callback invocations in try-catch blocks to prevent silent failures and ensure all observers are notified even when one throws an exception. Previously, exceptions in observer callbacks would either: - Be silently discarded (method channel handlers) - Prevent subsequent observers from being notified Now, exceptions are properly reported via FlutterError.reportError and all observers continue to receive notifications. Fixes flutter#180434 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md [![talabat.com](https://img.shields.io/badge/talabat.com-contributions-FF5A00?style=flat&logo=flutter&logoColor=white)](https://www.talabat.com) [![Talabat Flutter PRs](https://img.shields.io/badge/Talabat_Flutter_PRs-10%20merged-97ca00?style=flat&logo=flutter&logoColor=white)](https://github.com/search?q=org%3Aflutter+talabat&type=pullrequests) --------- Co-authored-by: Mohellebi Abdessalem <116356835+AbdeMohlbi@users.noreply.github.com>
1 parent 00bdc4e commit 95da3aa

File tree

2 files changed

+859
-24
lines changed

2 files changed

+859
-24
lines changed

packages/flutter/lib/src/widgets/binding.dart

Lines changed: 232 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -843,11 +843,24 @@ mixin WidgetsBinding
843843
Future<AppExitResponse> handleRequestAppExit() async {
844844
var didCancel = false;
845845
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
846-
if ((await observer.didRequestAppExit()) == AppExitResponse.cancel) {
847-
didCancel = true;
848-
// Don't early return. For the case where someone is just using the
849-
// observer to know when exit happens, we want to call all the
850-
// observers, even if we already know we're going to cancel.
846+
try {
847+
if ((await observer.didRequestAppExit()) == AppExitResponse.cancel) {
848+
didCancel = true;
849+
// Don't early return. For the case where someone is just using the
850+
// observer to know when exit happens, we want to call all the
851+
// observers, even if we already know we're going to cancel.
852+
}
853+
} catch (exception, stack) {
854+
FlutterError.reportError(
855+
FlutterErrorDetails(
856+
exception: exception,
857+
stack: stack,
858+
library: 'widgets library',
859+
context: ErrorDescription(
860+
'while dispatching notifications for WidgetsBindingObserver.didRequestAppExit',
861+
),
862+
),
863+
);
851864
}
852865
}
853866
return didCancel ? AppExitResponse.cancel : AppExitResponse.exit;
@@ -857,31 +870,83 @@ mixin WidgetsBinding
857870
void handleMetricsChanged() {
858871
super.handleMetricsChanged();
859872
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
860-
observer.didChangeMetrics();
873+
try {
874+
observer.didChangeMetrics();
875+
} catch (exception, stack) {
876+
FlutterError.reportError(
877+
FlutterErrorDetails(
878+
exception: exception,
879+
stack: stack,
880+
library: 'widgets library',
881+
context: ErrorDescription(
882+
'while dispatching notifications for WidgetsBindingObserver.didChangeMetrics',
883+
),
884+
),
885+
);
886+
}
861887
}
862888
}
863889

864890
@override
865891
void handleTextScaleFactorChanged() {
866892
super.handleTextScaleFactorChanged();
867893
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
868-
observer.didChangeTextScaleFactor();
894+
try {
895+
observer.didChangeTextScaleFactor();
896+
} catch (exception, stack) {
897+
FlutterError.reportError(
898+
FlutterErrorDetails(
899+
exception: exception,
900+
stack: stack,
901+
library: 'widgets library',
902+
context: ErrorDescription(
903+
'while dispatching notifications for WidgetsBindingObserver.didChangeTextScaleFactor',
904+
),
905+
),
906+
);
907+
}
869908
}
870909
}
871910

872911
@override
873912
void handlePlatformBrightnessChanged() {
874913
super.handlePlatformBrightnessChanged();
875914
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
876-
observer.didChangePlatformBrightness();
915+
try {
916+
observer.didChangePlatformBrightness();
917+
} catch (exception, stack) {
918+
FlutterError.reportError(
919+
FlutterErrorDetails(
920+
exception: exception,
921+
stack: stack,
922+
library: 'widgets library',
923+
context: ErrorDescription(
924+
'while dispatching notifications for WidgetsBindingObserver.didChangePlatformBrightness',
925+
),
926+
),
927+
);
928+
}
877929
}
878930
}
879931

880932
@override
881933
void handleAccessibilityFeaturesChanged() {
882934
super.handleAccessibilityFeaturesChanged();
883935
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
884-
observer.didChangeAccessibilityFeatures();
936+
try {
937+
observer.didChangeAccessibilityFeatures();
938+
} catch (exception, stack) {
939+
FlutterError.reportError(
940+
FlutterErrorDetails(
941+
exception: exception,
942+
stack: stack,
943+
library: 'widgets library',
944+
context: ErrorDescription(
945+
'while dispatching notifications for WidgetsBindingObserver.didChangeAccessibilityFeatures',
946+
),
947+
),
948+
);
949+
}
885950
}
886951
}
887952

@@ -907,7 +972,20 @@ mixin WidgetsBinding
907972
@mustCallSuper
908973
void dispatchLocalesChanged(List<Locale>? locales) {
909974
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
910-
observer.didChangeLocales(locales);
975+
try {
976+
observer.didChangeLocales(locales);
977+
} catch (exception, stack) {
978+
FlutterError.reportError(
979+
FlutterErrorDetails(
980+
exception: exception,
981+
stack: stack,
982+
library: 'widgets library',
983+
context: ErrorDescription(
984+
'while dispatching notifications for WidgetsBindingObserver.didChangeLocales',
985+
),
986+
),
987+
);
988+
}
911989
}
912990
}
913991

@@ -982,8 +1060,21 @@ mixin WidgetsBinding
9821060
@visibleForTesting
9831061
Future<bool> handlePopRoute() async {
9841062
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
985-
if (await observer.didPopRoute()) {
986-
return true;
1063+
try {
1064+
if (await observer.didPopRoute()) {
1065+
return true;
1066+
}
1067+
} catch (exception, stack) {
1068+
FlutterError.reportError(
1069+
FlutterErrorDetails(
1070+
exception: exception,
1071+
stack: stack,
1072+
library: 'widgets library',
1073+
context: ErrorDescription(
1074+
'while dispatching notifications for WidgetsBindingObserver.didPopRoute',
1075+
),
1076+
),
1077+
);
9871078
}
9881079
}
9891080
SystemNavigator.pop();
@@ -997,8 +1088,21 @@ mixin WidgetsBinding
9971088
_backGestureObservers.clear();
9981089
final backEvent = PredictiveBackEvent.fromMap(arguments);
9991090
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
1000-
if (observer.handleStartBackGesture(backEvent)) {
1001-
_backGestureObservers.add(observer);
1091+
try {
1092+
if (observer.handleStartBackGesture(backEvent)) {
1093+
_backGestureObservers.add(observer);
1094+
}
1095+
} catch (exception, stack) {
1096+
FlutterError.reportError(
1097+
FlutterErrorDetails(
1098+
exception: exception,
1099+
stack: stack,
1100+
library: 'widgets library',
1101+
context: ErrorDescription(
1102+
'while dispatching notifications for WidgetsBindingObserver.handleStartBackGesture',
1103+
),
1104+
),
1105+
);
10021106
}
10031107
}
10041108
return _backGestureObservers.isNotEmpty;
@@ -1011,7 +1115,20 @@ mixin WidgetsBinding
10111115

10121116
final backEvent = PredictiveBackEvent.fromMap(arguments);
10131117
for (final WidgetsBindingObserver observer in _backGestureObservers) {
1014-
observer.handleUpdateBackGestureProgress(backEvent);
1118+
try {
1119+
observer.handleUpdateBackGestureProgress(backEvent);
1120+
} catch (exception, stack) {
1121+
FlutterError.reportError(
1122+
FlutterErrorDetails(
1123+
exception: exception,
1124+
stack: stack,
1125+
library: 'widgets library',
1126+
context: ErrorDescription(
1127+
'while dispatching notifications for WidgetsBindingObserver.handleUpdateBackGestureProgress',
1128+
),
1129+
),
1130+
);
1131+
}
10151132
}
10161133
}
10171134

@@ -1026,13 +1143,39 @@ mixin WidgetsBinding
10261143
return;
10271144
}
10281145
for (final WidgetsBindingObserver observer in _backGestureObservers) {
1029-
observer.handleCommitBackGesture();
1146+
try {
1147+
observer.handleCommitBackGesture();
1148+
} catch (exception, stack) {
1149+
FlutterError.reportError(
1150+
FlutterErrorDetails(
1151+
exception: exception,
1152+
stack: stack,
1153+
library: 'widgets library',
1154+
context: ErrorDescription(
1155+
'while dispatching notifications for WidgetsBindingObserver.handleCommitBackGesture',
1156+
),
1157+
),
1158+
);
1159+
}
10301160
}
10311161
}
10321162

10331163
void _handleCancelBackGesture() {
10341164
for (final WidgetsBindingObserver observer in _backGestureObservers) {
1035-
observer.handleCancelBackGesture();
1165+
try {
1166+
observer.handleCancelBackGesture();
1167+
} catch (exception, stack) {
1168+
FlutterError.reportError(
1169+
FlutterErrorDetails(
1170+
exception: exception,
1171+
stack: stack,
1172+
library: 'widgets library',
1173+
context: ErrorDescription(
1174+
'while dispatching notifications for WidgetsBindingObserver.handleCancelBackGesture',
1175+
),
1176+
),
1177+
);
1178+
}
10361179
}
10371180
}
10381181

@@ -1052,8 +1195,21 @@ mixin WidgetsBinding
10521195
Future<bool> handlePushRoute(String route) async {
10531196
final routeInformation = RouteInformation(uri: Uri.parse(route));
10541197
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
1055-
if (await observer.didPushRouteInformation(routeInformation)) {
1056-
return true;
1198+
try {
1199+
if (await observer.didPushRouteInformation(routeInformation)) {
1200+
return true;
1201+
}
1202+
} catch (exception, stack) {
1203+
FlutterError.reportError(
1204+
FlutterErrorDetails(
1205+
exception: exception,
1206+
stack: stack,
1207+
library: 'widgets library',
1208+
context: ErrorDescription(
1209+
'while dispatching notifications for WidgetsBindingObserver.didPushRouteInformation',
1210+
),
1211+
),
1212+
);
10571213
}
10581214
}
10591215
return false;
@@ -1065,8 +1221,21 @@ mixin WidgetsBinding
10651221
state: routeArguments['state'] as Object?,
10661222
);
10671223
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
1068-
if (await observer.didPushRouteInformation(routeInformation)) {
1069-
return true;
1224+
try {
1225+
if (await observer.didPushRouteInformation(routeInformation)) {
1226+
return true;
1227+
}
1228+
} catch (exception, stack) {
1229+
FlutterError.reportError(
1230+
FlutterErrorDetails(
1231+
exception: exception,
1232+
stack: stack,
1233+
library: 'widgets library',
1234+
context: ErrorDescription(
1235+
'while dispatching notifications for WidgetsBindingObserver.didPushRouteInformation',
1236+
),
1237+
),
1238+
);
10701239
}
10711240
}
10721241
return false;
@@ -1100,23 +1269,62 @@ mixin WidgetsBinding
11001269
void handleAppLifecycleStateChanged(AppLifecycleState state) {
11011270
super.handleAppLifecycleStateChanged(state);
11021271
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
1103-
observer.didChangeAppLifecycleState(state);
1272+
try {
1273+
observer.didChangeAppLifecycleState(state);
1274+
} catch (exception, stack) {
1275+
FlutterError.reportError(
1276+
FlutterErrorDetails(
1277+
exception: exception,
1278+
stack: stack,
1279+
library: 'widgets library',
1280+
context: ErrorDescription(
1281+
'while dispatching notifications for WidgetsBindingObserver.didChangeAppLifecycleState',
1282+
),
1283+
),
1284+
);
1285+
}
11041286
}
11051287
}
11061288

11071289
@override
11081290
void handleViewFocusChanged(ViewFocusEvent event) {
11091291
super.handleViewFocusChanged(event);
11101292
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
1111-
observer.didChangeViewFocus(event);
1293+
try {
1294+
observer.didChangeViewFocus(event);
1295+
} catch (exception, stack) {
1296+
FlutterError.reportError(
1297+
FlutterErrorDetails(
1298+
exception: exception,
1299+
stack: stack,
1300+
library: 'widgets library',
1301+
context: ErrorDescription(
1302+
'while dispatching notifications for WidgetsBindingObserver.didChangeViewFocus',
1303+
),
1304+
),
1305+
);
1306+
}
11121307
}
11131308
}
11141309

11151310
@override
11161311
void handleMemoryPressure() {
11171312
super.handleMemoryPressure();
11181313
for (final observer in List<WidgetsBindingObserver>.of(_observers)) {
1119-
observer.didHaveMemoryPressure();
1314+
try {
1315+
observer.didHaveMemoryPressure();
1316+
} catch (exception, stack) {
1317+
FlutterError.reportError(
1318+
FlutterErrorDetails(
1319+
exception: exception,
1320+
stack: stack,
1321+
library: 'widgets library',
1322+
context: ErrorDescription(
1323+
'while dispatching notifications for WidgetsBindingObserver.didHaveMemoryPressure',
1324+
),
1325+
),
1326+
);
1327+
}
11201328
}
11211329
}
11221330

0 commit comments

Comments
 (0)