jatin
jatin

Reputation: 385

Unable to display SnackBar

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

Answers (4)

Devqxz
Devqxz

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

Jitesh Mohite
Jitesh Mohite

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

CopsOnRoad
CopsOnRoad

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

Kris
Kris

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

Related Questions