Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve SnackBar error message when shown during build #106658

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 38 additions & 5 deletions packages/flutter/lib/src/material/scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down
17 changes: 17 additions & 0 deletions packages/flutter/test/material/scaffold_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down