10

I'm trying to display a SnackBar after performing an action from the AppBar. The AppBar cannot be built from a builder so it can't access is Scaffold ancestor. I know we can use a GlobalKey object to access the context whenever we want, but I would like to know if there is a solution without using the GlobalKey. I found some github issues and pull-request, but I can't find a solution from them => https://github.com/flutter/flutter/issues/4581 and https://github.com/flutter/flutter/pull/9380

Some more context: I have an Appbar with a PopupMenuButton, which have one item. When the user click on this item I display a dialog which the showDialog method and if the user clicks on "ok" I want to display a SnackBar

Quentin
  • 586
  • 8
  • 10

3 Answers3

14

You can use the Builder widget

Example:

Scaffold(
  appBar: AppBar(
    actions: <Widget>[
      Builder(
        builder: (BuildContext context) {
          return IconButton(
            icon: const Icon(Icons.message),
            onPressed: () {
              final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
              Scaffold.of(context).showSnackBar(snackBar);
            },
          );
        },
      ),
    ],
  )
);
Migo
  • 206
  • 2
  • 4
9

The Scaffold.appBar parameter requires a PreferredSizeWidget, so you can have a Builder there like this:

appBar: PreferredSize(
  preferredSize: Size.fromHeight(56),
  child: Builder(
    builder: (context) => AppBar(...),
  ),
),
Dario Ielardi
  • 807
  • 9
  • 24
  • I tried your solution, but when I pass the context to the method which build my alert, and then when I try to display the SnackBar, with this context, I still have the `Scaffold.of() called with a context that does not contain a Scaffold` error. – Quentin Nov 29 '18 at 20:43
  • 1
    This solution worked for me. My call: actions: [ new IconButton(icon: const Icon(Icons.settings), onPressed: () =>_handleSettings(Scaffold.of(context))), ], – AlanKley Mar 06 '19 at 14:54
  • This works as is, `builder: (context) => AppBar(...),`: make sure you write your AppBar code inside the brackets right there. It stops working if you extract the AppBar into a variable or method. – WSBT Mar 12 '19 at 06:11
1

An option is to use two contexts in the dialog and use the context passed to the dialog to search for the Scaffold.

When you show a dialog, you are displaying a completely different page/route which is outside the scope of the calling page. So no scaffold is available.

Below you have a working example where you use the scope of the first page. The problem, though, is that the SnackBar is not removed.

If instead you use a GlobalKey to get the Scaffold the problem is the same.

I would consider not using a Snackbar in this case, because it is associated to the page below. It is even greyed out by the dialog shadow.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  _showDialog(BuildContext context1) {
    return showDialog(
        context: context1,
        builder: (BuildContext context) {
          return AlertDialog(
            content: Text("Dialog"),
            actions: <Widget>[
              new FlatButton(
                child: new Text("OK"),
                onPressed: () => Scaffold.of(context1).showSnackBar(SnackBar(
                      content: Text("Pressed"),
                    )),
              ),
            ],
          );
        });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Test"),
        actions: <Widget>[
          PopupMenuButton(
            itemBuilder: (BuildContext context) {
              return <PopupMenuEntry>[
                PopupMenuItem(
                  child: ListTile(
                    title: Text('Show dialog'),
                    onTap: () => _showDialog(context),
                  ),
                ),
              ];
            },
          )
        ],
      ),
    );
  }
}
chemamolins
  • 19,400
  • 5
  • 54
  • 47
  • I added more context to my question. I didn't test your solution but I want to display the `SnackBar` after a dialog from a `PopupMenuButton` item – Quentin Nov 29 '18 at 20:49
  • I have changed my answer according to the additional information you have provided. – chemamolins Nov 29 '18 at 21:27
  • 1
    I tested it. I can't find the way to integrate it with my code due to some weird behaviors but it's working. But there are a few problems: the SnackBar does not disappear and the action menu does not disappear by itself. – Quentin Nov 30 '18 at 12:48