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

116,151

Solution 1

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)

Solution 2

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
  }
}

Solution 3

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);
          },
        ),
     );
   }
}

Solution 4

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:

Solution 5

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")));

Share:
116,151
Figen Güngör
Author by

Figen Güngör

Updated on December 26, 2021

Comments

  • Figen Güngör
    Figen Güngör over 2 years

    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); 
    
  • CodeGrue
    CodeGrue almost 6 years
    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?
  • CopsOnRoad
    CopsOnRoad over 5 years
    @RémiRousselet, How many types of context are there in Flutter? Like you said context of widget, context of a child of Scaffold.
  • Rémi Rousselet
    Rémi Rousselet over 5 years
    @CopsOnRoad Only one. Scaffold is a widget too.
  • jbg
    jbg over 5 years
    @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()
  • temirbek
    temirbek about 5 years
    the code from question is same as the official docs (flutter.dev/docs/cookbook/design/snackbars) which doesn't use Builder, is it a bug in offical documentation?
  • Rémi Rousselet
    Rémi Rousselet about 5 years
    Builder is not the only way obtain a different BuildContext.
  • stevenspiel
    stevenspiel almost 5 years
    @RémiRousselet What are other ways to obtain a different BuildContext? I can think of using GlobalKeys, but what are some more?
  • Rémi Rousselet
    Rémi Rousselet almost 5 years
    Split your widget into multiple ones.
  • ExactaBox
    ExactaBox over 4 years
    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
    ExactaBox over 4 years
    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() ...
  • user2233706
    user2233706 about 4 years
    Why is this inefficient?
  • Lebohang Mbele
    Lebohang Mbele about 4 years
    @user2233706 This is mentioned in the documentation of GlobalKey here. Also this post might shed more light.
  • Pankaj
    Pankaj over 3 years
    _scaffoldKey.currentState.showSnackBar(snackBar); has been depreciated.
  • Pankaj
    Pankaj over 3 years
    Both approached have been depreciated.
  • Pankaj
    Pankaj over 3 years
    Both approached have been depreciated
  • Keith DC
    Keith DC almost 3 years
    @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
    Keith DC almost 3 years
    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
    Keith DC almost 3 years
    Never mind. I found the extended answer by @Deczaloth below. Glad I caught your comment though.
  • Keith DC
    Keith DC almost 3 years
    Regarding deprecation: see this answer by @Deczaloth. The new approach calls on the ScaffoldMessenger to show the SnackBar.
  • Keith DC
    Keith DC almost 3 years
    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
    Keith DC almost 3 years
    Regarding deprecation: see this answer by @Deczaloth. "The new approach calls on the ScaffoldMessenger to show the SnackBar.
  • Vipin Krishna
    Vipin Krishna about 2 years
    do not forget to add : 'drawer: NavigationDrawer()'