363

As you can see, my button is inside the Scaffold's body. But I get this exception:

Scaffold.of() called with a context that does not contain a Scaffold.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SnackBar Playground'),
      ),
      body: Center(
        child: RaisedButton(
          color: Colors.pink,
          textColor: Colors.white,
          onPressed: _displaySnackBar(context),
          child: Text('Display SnackBar'),
        ),
      ),
    );
  }
}

_displaySnackBar(BuildContext context) {
  final snackBar = SnackBar(content: Text('Are you talkin\' to me?'));
  Scaffold.of(context).showSnackBar(snackBar);
}

EDIT:

I found another solution to this problem. If we give the Scaffold a key which is the GlobalKey<ScaffoldState>, we can display the SnackBar as following without the need to wrap our body within the Builder widget. The widget which returns the Scaffold should be a Stateful widget though.

 _scaffoldKey.currentState.showSnackBar(snackbar); 
1

16 Answers 16

467

This exception happens because you are using the context of the widget that instantiated Scaffold. Not the context of a child of Scaffold.

You can solve this by just using a different context :

Scaffold(
    appBar: AppBar(
        title: Text('SnackBar Playground'),
    ),
    body: Builder(
        builder: (context) => 
            Center(
            child: RaisedButton(
            color: Colors.pink,
            textColor: Colors.white,
            onPressed: () => _displaySnackBar(context),
            child: Text('Display SnackBar'),
            ),
        ),
    ),
);

Note that while we're using Builder here, this is not the only way to obtain a different BuildContext.

It is also possible to extract the subtree into a different Widget (usually using extract widget refactor)

10
  • 1
    I thought with .of(context) it's supposed to travel up the widget hierarchy until it encounters one of the given type, in this case, Scaffold, which it should encounter before it reaches "Widget build(..". Does it not work this way?
    – CodeGrue
    Aug 4, 2018 at 1:15
  • @RémiRousselet, How many types of context are there in Flutter? Like you said context of widget, context of a child of Scaffold.
    – CopsOnRoad
    Sep 27, 2018 at 6:54
  • 3
    @CodeGrue it does work that way, but the context being passed to Scaffold.of() was "above" the Scaffold in the hierarchy, since it was the context passed to HomePage.build()
    – jbg
    Jan 15, 2019 at 0:55
  • 2
    Split your widget into multiple ones. Jun 20, 2019 at 20:19
  • 1
    For an updated approach, see this answer by @Deczaloth. "The new approach calls on the ScaffoldMessenger to show the SnackBar. In this case, the Builder is no longer required to provide a new scope with a BuildContext that is 'under' the Scaffold."
    – Keith DC
    Jun 3, 2021 at 4:19
198

You can use a GlobalKey. The only downside is that using GlobalKey might not be the most efficient way of doing this.

A good thing about this is that you can also pass this key to other custom widgets class that do not contain any scaffold. See(here)

class HomePage extends StatelessWidget {
  final _scaffoldKey = GlobalKey<ScaffoldState>(); \\ new line
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,                           \\ new line
      appBar: AppBar(
        title: Text('SnackBar Playground'),
      ),
      body: Center(
        child: RaisedButton(
          color: Colors.pink,
          textColor: Colors.white,
          onPressed: _displaySnackBar(context),
          child: Text('Display SnackBar'),
        ),
      ),
    );
  }
  _displaySnackBar(BuildContext context) {
    final snackBar = SnackBar(content: Text('Are you talkin\' to me?'));
    _scaffoldKey.currentState.showSnackBar(snackBar);   \\ edited line
  }
}
6
  • 1
    Can I ask how to replicate this when your tree is comprised of multiple widgets defined in multiple files? _scaffoldKey is private due to the leading underscore. But even if it weren't, it is inside the HomePage class and therefore unavailable without instantiating a new HomePage somewhere down the tree, seemingly creating a circular reference.
    – ExactaBox
    Dec 24, 2019 at 4:59
  • 1
    to answer my own question: create a singleton class with a GlobalKey<ScaffoldState> property, edit the constructor of the Widget with the scaffold to set the singleton's key property, then in the child widget's tree we can call something like mySingleton.instance.key.currentState.showSnackBar() ...
    – ExactaBox
    Dec 24, 2019 at 13:19
  • Why is this inefficient? Mar 6, 2020 at 22:29
  • 1
    @user2233706 This is mentioned in the documentation of GlobalKey here. Also this post might shed more light. Mar 11, 2020 at 14:02
  • 1
    _scaffoldKey.currentState.showSnackBar(snackBar); has been depreciated.
    – Pankaj
    Jan 11, 2021 at 3:41
91

You can solve this problem in two ways:

1) Using Builder widget

Scaffold(
    appBar: AppBar(
        title: Text('My Profile'),
    ),
    body: Builder(
        builder: (ctx) => RaisedButton(
            textColor: Colors.red,
            child: Text('Submit'),
            onPressed: () {
                 Scaffold.of(ctx).showSnackBar(SnackBar(content: Text('Profile Save'),),);
            }               
        ),
    ),
);

2) Using GlobalKey

class HomePage extends StatelessWidget {
  
  final globalKey = GlobalKey<ScaffoldState>();
  
  @override
  Widget build(BuildContext context) {
     return Scaffold(
       key: globalKey,
       appBar: AppBar(
          title: Text('My Profile'),
       ),
       body:  RaisedButton(
          textColor: Colors.red,
          child: Text('Submit'),
          onPressed: (){
               final snackBar = SnackBar(content: Text('Profile saved'));
               globalKey.currentState.showSnackBar(snackBar);
          },
        ),
     );
   }
}
4
  • 3
    Both approached have been depreciated.
    – Pankaj
    Jan 11, 2021 at 3:42
  • @Pankaj Would you happen to have a source on both of these being deprecated? The Builder widget class is the top accepted answer, and the 2nd top answer uses GlobalKey (which has been noted can be "expensive" as well as a reactive antipattern). And although this answer appears redundant to the top two answers, I can't seem to find a reference to either being deprecated. Thanks for any insights.
    – Keith DC
    Jun 3, 2021 at 2:54
  • For the first solution, I do know Scaffold.of(context).showSnackBar(mySnackBar); has been replaced by ScaffoldMessenger.of(context).showSnackBar(mySnackBar);. Is that what you were referring to with solution 1 being deprecated? EDIT: Oh! Is this why? "...In this case, the Builder is no longer required to provide a new scope with a BuildContext that is 'under' the Scaffold."
    – Keith DC
    Jun 3, 2021 at 3:22
  • 1
    Never mind. I found the extended answer by @Deczaloth below. Glad I caught your comment though.
    – Keith DC
    Jun 3, 2021 at 4:04
35

UPDATE - 2021

Scaffold.of(context) is deprecated in favor of ScaffoldMessenger.

Check this from the documentation of method:

The ScaffoldMessenger now handles SnackBars in order to persist across routes and always be displayed on the current Scaffold. By default, a root ScaffoldMessenger is included in the MaterialApp, but you can create your own controlled scope for the ScaffoldMessenger to further control which Scaffolds receive your SnackBars.

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Demo')
    ),
    body: Builder(
      // Create an inner BuildContext so that the onPressed methods
      // can refer to the Scaffold with Scaffold.of().
      builder: (BuildContext context) {
         return Center(
          child: RaisedButton(
            child: Text('SHOW A SNACKBAR'),
            onPressed: () {
              ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                content: Text('Hello!'),
              ));
            },
          ),
        );
      },
    ),
  );
}

You can check the detailed deprecation and new approach here:

0
19

Simple way to solving this issue will be creating a key for your scaffold like this final with the following code:

First: GlobalKey<ScaffoldState>() _scaffoldKey = GlobalKey<ScaffoldState> ();

Scecond: Assign the Key to your Scaffold key: _scaffoldKey

Third: Call the Snackbar using _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Welcome")));

2
  • 2
    Both approached have been depreciated
    – Pankaj
    Jan 11, 2021 at 3:42
  • 1
    Regarding deprecation: see this answer by @Deczaloth. "The new approach calls on the ScaffoldMessenger to show the SnackBar.
    – Keith DC
    Jun 3, 2021 at 4:23
19

UPDATE: recommended approach by Flutter (as of 20.12.2022)...

To show a Snackbar you should be using:

ScaffoldMessenger

From the docu we read

The SnackBar API within the Scaffold is now handled by the ScaffoldMessenger, one of which is available by default within the context of a MaterialApp

So, with ScaffoldMessenger now you will be able to write code like

Scaffold(
  key: scaffoldKey,
  body: GestureDetector(
    onTap: () {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: const Text('snack'),
        duration: const Duration(seconds: 1),
        action: SnackBarAction(
          label: 'ACTION',
          onPressed: () { },
        ),
      ));
    },
    child: const Text('SHOW SNACK'),
  ),
);

Now, again in the docu we can see that

When presenting a SnackBar during a transition, the SnackBar will complete a Hero animation, moving smoothly to the next page.

The ScaffoldMessenger creates a scope in which all descendant Scaffolds register to receive SnackBars, which is how they persist across these transitions. When using the root ScaffoldMessenger provided by the MaterialApp, all descendant Scaffolds receive SnackBars, unless a new ScaffoldMessenger scope is created further down the tree. By instantiating your own ScaffoldMessenger, you can control which Scaffolds receive SnackBars, and which do not based on the context of your application.


ORIGINAL ANSWER

The very behavior you are experiencing is even referred to as a "tricky case" in the Flutter documentation.

How To Fix

The issue is fixed in different ways as you can see from other answers posted here. For instance, the piece of documentation i refer to solves the issue by using a Builder which creates

an inner BuildContext so that the onPressed methods can refer to the Scaffold with Scaffold.of().

Thus a way to call showSnackBar from Scaffold would be

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Demo')),
    body: Builder(
      builder: (BuildContext innerContext) {
        return FlatButton(
          child: Text('BUTTON'),
          onPressed: () {
            Scaffold.of(innerContext).showSnackBar(SnackBar(
              content: Text('Hello.')
            ));
          }
        );
      }
    )
  );
}

Now some detail for the curious reader

I myself found quite instructive to explore the Flutter documentation by simply (Android Studio) setting the cursor on a piece of code (Flutter class, method, etc.) and pressing ctrl+B to be shown the documentation for that specific piece.

The particular problem you are facing is mentioned in the docu for BuildContext, where can be read

Each widget has its own BuildContext, which becomes the parent of the widget returned by the [...].build function.

So, this means that in our case context will be the parent of our Scaffold widget when it is created (!). Further, the docu for Scaffold.of says that it returns

The state from the closest [Scaffold] instance of this class that encloses the given context.

But in our case, context does not encloses (yet) a Scaffold (it has not yet been built). There is where Builder comes into action!

Once again, the docu illuminates us. There we can read

[The Builder class, is simply] A platonic widget that calls a closure to obtain its child widget.

Hey, wait a moment, what!? Ok, i admit: that is not helping a lot... But it is enough to say (following another SO thread) that

The purpose of the Builder class is simply to build and return child widgets.

So now it all becomes clear! By calling Builder inside Scaffold we are building the Scaffold in order to be able to obtain its own context, and armed with that innerContext we can finally call Scaffold.of(innerContext)

An annotated version of the code above follows

@override
Widget build(BuildContext context) {
  // here, Scaffold.of(context) returns null
  return Scaffold(
    appBar: AppBar(title: Text('Demo')),
    body: Builder(
      builder: (BuildContext innerContext) {
        return FlatButton(
          child: Text('BUTTON'),
          onPressed: () {
            // here, Scaffold.of(innerContext) returns the locally created Scaffold
            Scaffold.of(innerContext).showSnackBar(SnackBar(
              content: Text('Hello.')
            ));
          }
        );
      }
    )
  );
}
0
15

Use ScaffoldMessenger (Recommended)

var snackBar = SnackBar(content: Text('Hi there'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);

Example (Without Builder or GlobalKey)

Scaffold(
  body: ElevatedButton(
    onPressed: () {
      var snackBar = SnackBar(content: Text('Hello World'));
      ScaffoldMessenger.of(context).showSnackBar(snackBar);
    },
    child: Text('Show SnackBar'),
  ),
)
0
12

From Flutter version 1.23-18.1.pre you can use ScaffoldMessenger

final mainScaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();

class Main extends StatelessWidget {
  @override
  Widget build(BuildContext) {
    return MaterialApp(
      ...
      scaffoldMessengerKey: mainScaffoldMessengerKey
      ...
    );
  }
}

Somewhere inside app:

mainScaffoldMessengerKey.currentState.showSnackBar(Snackbar(...));
0
7

A more efficient solution is to split your build function into several widgets. This introduce a 'new context', from which you can obtain Scaffold

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Scaffold.of example.')),
        body: MyScaffoldBody(),
      ),
    );
  }
}

class MyScaffoldBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
          child: Text('Show a snackBar'),
          onPressed: () {
            Scaffold.of(context).showSnackBar(
              SnackBar(
                content: Text('Have a Snack'),
              ),
            );
          }),
    );
  }
}
0
6

I may be late. But this will help someone too. Add a _key under the Scaffold. Then use that _key to call the openDrawer method.

return Scaffold(
  key: _scaffoldKey, //this is the key
  endDrawer: Drawer(),
  appBar: AppBar( 
//all codes for appbar here
actions: [
IconButton(
        splashRadius: 20,
        icon: Icon(Icons.settings),
        onPressed: () {
          _scaffoldKey.currentState.openEndDrawer(); // this is it
       
        },
      ),]
0
5

I wouldn't bother using the default snackbar, because you can import a flushbar package, which enables greater customizability:

https://pub.dev/packages/flushbar

For example:

Flushbar(
                  title:  "Hey Ninja",
                  message:  "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
                  duration:  Duration(seconds: 3),              
                )..show(context);
0
5

Maybe there is a solution in this code.

ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("SnackBar Message")));
3

here we use a builder to wrap in another widget where we need snackbar

Builder(builder: (context) => GestureDetector(
    onTap: () {
        Scaffold.of(context).showSnackBar(SnackBar(
            content: Text('Your Services have been successfully created Snackbar'),
        ));
        
    },
    child: Container(...)))
3
           Expanded(
              child: Container(
                width: MediaQuery.of(context).size.width,
                height: MediaQuery.of(context).size.height,
                child: Builder(
                  builder: (context) => RaisedButton(
                    onPressed: () {
                        final snackBar = SnackBar(
                          content: Text("added to cart"),
                           action: SnackBarAction(
                            label: 'Undo',
                             onPressed: () {
                              // Some code to undo the change.
                            },
                          ),
                        );
                    },
                    textColor: Colors.white,
                    color: Colors.pinkAccent,
                    child: Text("Add To Cart",
                        style: TextStyle(
                            fontSize: 18, fontWeight: FontWeight.w600)),
                  ),
                ),
              ),
            )
0

Try this code:

Singleton.showInSnackBar(
    Scaffold.of(context).context, "Theme Changed Successfully");
// Just use Scaffold.of(context) before context!!
0

for me I had to wrap my widget with Scaffold

bonus tip: backgroundColor:Colors.transparnt

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.