Reputation: 385
I am new to Flutter and just playing around to understand the working of this framework. So the code below doesn't make much sense.
I am trying to display a snack bar as soon as the app loads by calling a child widget.
Here's the code :
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Demo')),
body: GetSnackBarWidget(),
),
);
}
}
class GetSnackBarWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final snackBar = SnackBar(content: Text("SnackBar"));
Scaffold.of(context).showSnackBar(snackBar);
return Text("returned");
}
}
But I am getting the following Exception :
I/flutter ( 3781): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 3781): The following assertion was thrown building GetSnackBarWidget(dirty):
I/flutter ( 3781): setState() or markNeedsBuild() called during build.
I/flutter ( 3781): This Scaffold widget cannot be marked as needing to build because the framework is already in the
I/flutter ( 3781): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter ( 3781): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter ( 3781): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter ( 3781): Otherwise, the framework might not visit this widget during this build phase.
I/flutter ( 3781): The widget on which setState() or markNeedsBuild() was called was:
I/flutter ( 3781): Scaffold(dependencies: [_LocalizationsScope-[GlobalKey#33728], Directionality, _InheritedTheme,
I/flutter ( 3781): MediaQuery], state: ScaffoldState#dbc9e(tickers: tracking 2 tickers))
I/flutter ( 3781): The widget which was currently being built when the offending call was made was:
I/flutter ( 3781): GetSnackBarWidget(dirty)
Can someone explain in detail what's happening?
Upvotes: 6
Views: 5492
Reputation: 111
Wrappig your call in a Future, like this below solves the issue.
Future(() {
final message = state.data["message"] ?? "";
if (message.length > 0) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(message)));
}
});
Upvotes: 1
Reputation: 34170
If you want to show SnackBar inside your widget you have to follow below code:
class _DelayWidgetState extends State<DelayWidget> {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("SbackBar Functionality"),
),
body: new Builder(builder: (BuildContext context) {
return new RaisedButton(
onPressed: () {
Scaffold.of(context).showSnackBar(
new SnackBar(
content: new Text("Sending Message"),
),
);
},
child: new Text("Click"),
);
}),
);
}
}
In the above example, another Builder added as Scaffold.of() called with a context that does not contain a Scaffold.
Upvotes: 0
Reputation: 267404
First of all it is not a good idea to put SnackBar
in your build()
method. You can try this solution which shows SnackBar
on app startup as you need.
class _HomePageState extends State<HomePage> {
GlobalKey<ScaffoldState> _key = GlobalKey();
@override
void initState() {
super.initState();
Timer.run(() => _key.currentState.showSnackBar(SnackBar(content: Text("Hi"),)));
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
appBar: AppBar(title: Text("App")),
body: Container(),
);
}
}
Upvotes: 0
Reputation: 3361
Essentially, you can't call Scaffold.of(context).showSnackBar(snackBar) directly while you are building the widget. If you have a button, you could use that for the onPressed method, because it wouldn't be called immediately as the widget is being built, but rather after some other asynchronous event.
The solution, if you have a snackbar to show right off the bat, is instead to tell the app to show the snackbar as soon as it is done building all the widgets. You can do this by using WidgetsBinding.instance.addPostFrameCallback() to add a callback method that will show the snackbar, like so:
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
...
return Scaffold(
key: _scaffoldKey,
...
//when you want to display the snackbar during a build method,
//use addPostFrameCallback
WidgetsBinding.instance.addPostFrameCallback((_) => _showMessage('snackbar message'));
...
void _showMessage(String message) {
_scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(message),));
}
Upvotes: 10