Improve SnackBar error message when shown during build (#106658)

This commit is contained in:
Bruno Leroux 2022-06-30 22:28:05 +02:00 committed by GitHub
parent 90d6303ff5
commit 0eed9adde7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 5 deletions

View File

@ -242,7 +242,7 @@ class ScaffoldMessengerState extends State<ScaffoldMessenger> with TickerProvide
// SNACKBAR API
/// Shows a [SnackBar] across all registered [Scaffold]s.
/// Shows a [SnackBar] across all registered [Scaffold]s.
///
/// A scaffold can show at most one snack bar at a time. If this function is
/// called while another snack bar is already visible, the given snack bar
@ -289,10 +289,43 @@ class ScaffoldMessengerState extends State<ScaffoldMessenger> with TickerProvide
},
null, // SnackBar doesn't use a builder function so setState() wouldn't rebuild it
);
setState(() {
_snackBars.addLast(controller);
});
_updateScaffolds();
try {
setState(() {
_snackBars.addLast(controller);
});
_updateScaffolds();
} catch (exception) {
assert (() {
if (exception is FlutterError) {
final String summary = exception.diagnostics.first.toDescription();
if (summary == 'setState() or markNeedsBuild() called during build.') {
final List<DiagnosticsNode> information = <DiagnosticsNode>[
ErrorSummary('The showSnackBar() method cannot be called during build.'),
ErrorDescription(
'The showSnackBar() method was called during build, which is '
'prohibited as showing snack bars requires updating state. Updating '
'state is not possible during build.',
),
ErrorHint(
'Instead of calling showSnackBar() during build, call it directly '
'in your on tap (and related) callbacks. If you need to immediately '
'show a snack bar, make the call in initState() or '
'didChangeDependencies() instead. Otherwise, you can also schedule a '
'post-frame callback using SchedulerBinding.addPostFrameCallback to '
'show the snack bar after the current frame.',
),
context.describeOwnershipChain(
'The ownership chain for the particular ScaffoldMessenger is',
),
];
throw FlutterError.fromParts(information);
}
}
return true;
}());
rethrow;
}
return controller;
}

View File

@ -2582,6 +2582,23 @@ void main() {
expect(isDrawerOpen, false);
expect(isEndDrawerOpen, false);
});
testWidgets('ScaffoldMessenger showSnackBar throws an intuitive error message if called during build', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext context) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('SnackBar')));
return const SizedBox.shrink();
},
),
),
));
final FlutterError error = tester.takeException() as FlutterError;
final ErrorSummary summary = error.diagnostics.first as ErrorSummary;
expect(summary.toString(), 'The showSnackBar() method cannot be called during build.');
});
}
class _GeometryListener extends StatefulWidget {