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

FlutterDartSnackbarScaffold

Flutter Problem Overview


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

Flutter Solutions


Solution 1 - Flutter

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

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

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

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

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

Solution 6 - Flutter

UPDATE 03.03.2021: Flutter 2.0 has just been released...

(As of historic value, i also let further below my original answer...)


Now... Enter...

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, using the brand new 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.')
            ));
          }
        );
      }
    )
  );
}

Solution 7 - Flutter

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'),
  ),
)

Solution 8 - Flutter

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(...));

Solution 9 - Flutter

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

Solution 10 - Flutter

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

Solution 11 - Flutter

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

Solution 12 - Flutter

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(...)))

Solution 13 - Flutter

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

Solution 14 - Flutter

Maybe there is a solution in this code.

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

Solution 15 - Flutter

Try this code:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFigen G&#252;ng&#246;rView Question on Stackoverflow
Solution 1 - FlutterRémi RousseletView Answer on Stackoverflow
Solution 2 - FlutterLebohang MbeleView Answer on Stackoverflow
Solution 3 - FlutterSanjayrajsinhView Answer on Stackoverflow
Solution 4 - FlutterSANATView Answer on Stackoverflow
Solution 5 - FlutterMohamed Abdul-MalikView Answer on Stackoverflow
Solution 6 - FlutterdeczalothView Answer on Stackoverflow
Solution 7 - FlutterCopsOnRoadView Answer on Stackoverflow
Solution 8 - FlutterBambinoUAView Answer on Stackoverflow
Solution 9 - FlutterDilshanView Answer on Stackoverflow
Solution 10 - FlutterAlexView Answer on Stackoverflow
Solution 11 - FlutterABHIMANGAL MSView Answer on Stackoverflow
Solution 12 - FlutterKrishna ChaitanyaView Answer on Stackoverflow
Solution 13 - Flutterkaran bisenView Answer on Stackoverflow
Solution 14 - FlutterLefView Answer on Stackoverflow
Solution 15 - FlutterSanket JadhavView Answer on Stackoverflow