Flutter: How to display a snackbar from an appbar action

3,940

Solution 1

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

Solution 2

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

Solution 3

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),
                  ),
                ),
              ];
            },
          )
        ],
      ),
    );
  }
}
Share:
3,940
Quentin
Author by

Quentin

Updated on December 08, 2022

Comments

  • Quentin
    Quentin over 1 year

    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
    Quentin over 5 years
    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
    Quentin over 5 years
    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
  • chemamolins
    chemamolins over 5 years
    I have changed my answer according to the additional information you have provided.
  • Quentin
    Quentin over 5 years
    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.
  • AlanKley
    AlanKley about 5 years
    This solution worked for me. My call: actions: <Widget>[ new IconButton(icon: const Icon(Icons.settings), onPressed: () =>_handleSettings(Scaffold.of(context))), ],
  • WSBT
    WSBT about 5 years
    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.
  • vishalknishad
    vishalknishad over 3 years
    this is what i was looking for