lonelearner
lonelearner

Reputation: 1837

Snackbar in SimpleDialog Flutter

I faced an error code below when adding a snackbar to an on-pressed method in my Simpledialog. [Scaffold.of() called with a context that does not contain a Scaffold.]

I would like to seek your advice on how to provide the correct context to resolve it.

import 'package:flutter/material.dart';



void main() {
  runApp(new MaterialApp(home: new AlertApp()));
}

class AlertApp extends StatefulWidget {
  @override
  _AlertAppState createState() => _AlertAppState();
}

class _AlertAppState extends State<AlertApp> {


  SimpleDialog _simdalog;

  void sDialog(){
    _simdalog = new SimpleDialog(
      title: new Text("Add To Shopping Cart"),
      children: <Widget>[
        new SimpleDialogOption(
          child: new Text("Yes"),
          onPressed: (){
            final snackBar = SnackBar(content: Text('Purchase Successful'));
            Scaffold.of(context).showSnackBar(snackBar);
          },
        ),
        new SimpleDialogOption(
          child: new Text("Close"),
          onPressed:() {Navigator.pop(context);},
        ),
      ],
    );
    showDialog(context: context, builder: (BuildContext context){
      return _simdalog;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: new Center(
        child: Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
        new RaisedButton(
            child: new Text("Add to Shopping Cart [Simple]"),
            onPressed:(){
              sDialog();
            }),
          ],
        ),
      ),
    );
  }
}

Upvotes: 2

Views: 7205

Answers (3)

Mr. ZeroOne
Mr. ZeroOne

Reputation: 954

You should create a Scaffold widget inside of showDialog and a Builder widget as child of the Scaffold and pass context as parameter.

 void sDialog({BuildContext context}){
     
    _simdalog = new SimpleDialog(
      title: new Text("Add To Shopping Cart"),
      children: <Widget>[
        new SimpleDialogOption(
          child: new Text("Yes"),
          onPressed: (){
            final snackBar = SnackBar(content: Text('Purchase Successful'));
            Scaffold.of(context).showSnackBar(snackBar);
          },
        ),
        new SimpleDialogOption(
          child: new Text("Close"),
          onPressed:() {Navigator.pop(context);},
        ),
      ],
    );
    showDialog(context: context, builder: (BuildContext context){
      return GestureDetector(
          onTap: (){Navigator.of(context).pop();},
          child: Scaffold(
            body: Builder(
                builder: (context){
                    return  _simdalog(context: context);
                }
            ),  
         ),);
    });
  }

Upvotes: 0

Wes1324
Wes1324

Reputation: 1123

You can return a bool from the showDialog method and use that to determine whether to show the snackbar:

void main() {
  runApp(MaterialApp(
    home: AlertApp(),
  ));
}

class AlertApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            MyShoppingButton(),
          ],
        ),
      ),
    );
  }
}

// Separate out the button from _AlertAppState so that the call to
// showSnackBar comes from a different BuildContext
class MyShoppingButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text("Add to Shopping Cart [Simple]"),
      // Use an async onPressed method so that we can wait for the
      // result from the dialog before deciding whether to show the snackbar
      onPressed: () async {
        bool result = await showDialog<bool>(
          context: context,
          builder: (BuildContext context) {
            return MyShoppingDialog();
          },
        );
        // Check if result is null below as Flutter will throw Exception if
        // tries determining whether to enter an if branch will a null boolean
        if (result != null && result) {
          final snackBar = SnackBar(content: Text('Purchase Successful'));
          Scaffold.of(context).showSnackBar(snackBar);
        }
      },
    );
  }
}

class MyShoppingDialog extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SimpleDialog(
      title: Text("Add To Shopping Cart"),
      children: <Widget>[
        SimpleDialogOption(
          child: Text("Yes"),
          onPressed: () {
            // Pop with a result of true so that MyShoppingButton
            // knows to show snackbar. In any other case
            // (including the user dismissing the dialog), MyShoppingButton
            // null receive null, and so will not show the snackbar
            Navigator.of(context).pop(true);
          },
        ),
        SimpleDialogOption(
          child: Text("Close"),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ],
    );
  }
}

Upvotes: 0

chunhunghan
chunhunghan

Reputation: 54367

Solution 1: as Mazin Ibrahim mentioned in comments Scaffold.of() called with a context that does not contain a Scaffold

final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
...
Scaffold(
  key: _scaffoldKey,
...
onPressed: () {

                      _scaffoldKey.currentState.showSnackBar(
                          SnackBar(
                            content: Text('Purchase Successful'),
                            duration: Duration(seconds: 3),
                          ));
              }

Solution 2: With package flushbar, you can also display notification on top
Flushbar link : https://github.com/AndreHaueisen/flushbar
Another suggestion to use flushbar How to show snackbar after navigator.pop(context) in Flutter? enter image description here

    Flushbar(
      title: "Hey Ninja",
      message: "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
      flushbarPosition: FlushbarPosition.TOP,
      flushbarStyle: FlushbarStyle.FLOATING,
      reverseAnimationCurve: Curves.decelerate,
      forwardAnimationCurve: Curves.elasticOut,
      backgroundColor: Colors.red,
      boxShadows: [BoxShadow(color: Colors.blue[800], offset: Offset(0.0, 2.0), blurRadius: 3.0)],
      backgroundGradient: LinearGradient(colors: [Colors.blueGrey, Colors.black]),
      isDismissible: false,
      duration: Duration(seconds: 4),
      icon: Icon(
        Icons.check,
        color: Colors.greenAccent,
      ),
      mainButton: FlatButton(
        onPressed: () {},
        child: Text(
          "CLAP",
          style: TextStyle(color: Colors.amber),
        ),
      ),
      showProgressIndicator: true,
      progressIndicatorBackgroundColor: Colors.blueGrey,
      titleText: Text(
        "Hello Hero",
        style: TextStyle(
            fontWeight: FontWeight.bold, fontSize: 20.0, color: Colors.yellow[600], fontFamily: "ShadowsIntoLightTwo"),
      ),
      messageText: Text(
        "You killed that giant monster in the city. Congratulations!",
        style: TextStyle(fontSize: 18.0, color: Colors.green, fontFamily: "ShadowsIntoLightTwo"),
      ),
    )..show(context);

Upvotes: 6

Related Questions