Skip to content

Commit 0eed9ad

Browse files
authored
Improve SnackBar error message when shown during build (#106658)
1 parent 90d6303 commit 0eed9ad

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

packages/flutter/lib/src/material/scaffold.dart

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ class ScaffoldMessengerState extends State<ScaffoldMessenger> with TickerProvide
242242

243243
// SNACKBAR API
244244

245-
/// Shows a [SnackBar] across all registered [Scaffold]s.
245+
/// Shows a [SnackBar] across all registered [Scaffold]s.
246246
///
247247
/// A scaffold can show at most one snack bar at a time. If this function is
248248
/// called while another snack bar is already visible, the given snack bar
@@ -289,10 +289,43 @@ class ScaffoldMessengerState extends State<ScaffoldMessenger> with TickerProvide
289289
},
290290
null, // SnackBar doesn't use a builder function so setState() wouldn't rebuild it
291291
);
292-
setState(() {
293-
_snackBars.addLast(controller);
294-
});
295-
_updateScaffolds();
292+
try {
293+
setState(() {
294+
_snackBars.addLast(controller);
295+
});
296+
_updateScaffolds();
297+
} catch (exception) {
298+
assert (() {
299+
if (exception is FlutterError) {
300+
final String summary = exception.diagnostics.first.toDescription();
301+
if (summary == 'setState() or markNeedsBuild() called during build.') {
302+
final List<DiagnosticsNode> information = <DiagnosticsNode>[
303+
ErrorSummary('The showSnackBar() method cannot be called during build.'),
304+
ErrorDescription(
305+
'The showSnackBar() method was called during build, which is '
306+
'prohibited as showing snack bars requires updating state. Updating '
307+
'state is not possible during build.',
308+
),
309+
ErrorHint(
310+
'Instead of calling showSnackBar() during build, call it directly '
311+
'in your on tap (and related) callbacks. If you need to immediately '
312+
'show a snack bar, make the call in initState() or '
313+
'didChangeDependencies() instead. Otherwise, you can also schedule a '
314+
'post-frame callback using SchedulerBinding.addPostFrameCallback to '
315+
'show the snack bar after the current frame.',
316+
),
317+
context.describeOwnershipChain(
318+
'The ownership chain for the particular ScaffoldMessenger is',
319+
),
320+
];
321+
throw FlutterError.fromParts(information);
322+
}
323+
}
324+
return true;
325+
}());
326+
rethrow;
327+
}
328+
296329
return controller;
297330
}
298331

packages/flutter/test/material/scaffold_test.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,6 +2582,23 @@ void main() {
25822582
expect(isDrawerOpen, false);
25832583
expect(isEndDrawerOpen, false);
25842584
});
2585+
2586+
testWidgets('ScaffoldMessenger showSnackBar throws an intuitive error message if called during build', (WidgetTester tester) async {
2587+
await tester.pumpWidget(MaterialApp(
2588+
home: Scaffold(
2589+
body: Builder(
2590+
builder: (BuildContext context) {
2591+
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('SnackBar')));
2592+
return const SizedBox.shrink();
2593+
},
2594+
),
2595+
),
2596+
));
2597+
2598+
final FlutterError error = tester.takeException() as FlutterError;
2599+
final ErrorSummary summary = error.diagnostics.first as ErrorSummary;
2600+
expect(summary.toString(), 'The showSnackBar() method cannot be called during build.');
2601+
});
25852602
}
25862603

25872604
class _GeometryListener extends StatefulWidget {

0 commit comments

Comments
 (0)