29

After an action (for example, "save"), user is returned to another page in the app.

I want to show a snackbar on the new page to confirm the action, but don't want it to show if the user navigated there without the action. So for example, if user saves "thing", the app sends them to "things" main page and shows the "saved" snackbar, but if they go to "things" main page some other way, I don't want the "saved" snackbar to be there.

Here is my code on the panel that saves, but the destination page does not show the snackbar — regardless where I place the snackbar (before or after navigation), it does not execute the snackbar.

Future<Null> saveThatThing() async {
  thing.save();
  thing = new Thing();
  Navigator.of(context).push(getSavedThingRoute());
  Scaffold.of(context).showSnackBar(
    new SnackBar(
      content: new Text('You saved the thing!'),
    ),
  );
}

What is the best way to do this?

Edric
  • 24,639
  • 13
  • 81
  • 91
Deborah
  • 4,316
  • 8
  • 31
  • 45
  • see https://stackoverflow.com/a/46108172/918867 . Use Future.delayed at initState in new page. – najeira Dec 23 '17 at 14:37
  • Also take a look at snack_bar_demo https://github.com/flutter/flutter/blob/master/examples/flutter_gallery/lib/demo/material/snack_bar_demo.dart#L50-L52 + Navigator.of().push() returns Future, so you can await it maybe? :/ https://docs.flutter.io/flutter/widgets/Navigator/push.html – tenhobi Dec 27 '17 at 22:15
  • I guess you also have the option of showing the snackbar thereby passing your information before navigating to the next screen by adding a delay before `Navigator.of(context).push(getSavedThingRoute())` – nonybrighto Aug 31 '18 at 23:08
  • FWIW, you should `await` that `push()`. Also, doing this in a `StatelessWidget` does not work currently. Changing the widget to be stateful worked for me. – Gazihan Alankus Dec 03 '18 at 00:05

8 Answers8

20

What about if you create key for the screen Scaffold like this (put this object inside the screen state class):

final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();

and then set this variable to the Scaffold key like this:

return new Scaffold(
  key: scaffoldKey,
  appBar: new AppBar(
    .......,
  ),

and at the end to show the SnackBar just use somewhere inside the screen state class this code :

scaffoldKey.currentState
    .showSnackBar(new SnackBar(content: new Text("Hello")));

or you can wrap the latest code in method like this :

void showInSnackBar(String value) {
scaffoldKey.currentState
    .showSnackBar(new SnackBar(content: new Text(value)));}

and then just call this method and pass the snack bar message inside.

If you want to show the snack bar message on the next screen, after you navigate away from the initial context, just call the snack bar method on the next screen, not on the first screen.

Hope this will help

Stoycho Andreev
  • 6,163
  • 1
  • 25
  • 27
  • Thank you @Deborah. I am not sure why was downvoted. If something does not work, pls let me know so I can try to help. – Stoycho Andreev Jan 02 '18 at 23:11
  • This makes total sense. Why can't the docs be this simple to comprehend? :facepalm – KhoPhi Jun 18 '18 at 21:52
  • Also make sure this scaffoldKey is not in a StatelessWidget. It will sometimes get recreated and you'll get surprise bugs. It goes into a State<>. – Gazihan Alankus Dec 03 '18 at 08:07
  • This works, but you have to use WidgetsBinding.instance.addPostFrameCallback in your initState (see this article: https://www.didierboelens.com/2019/04/addpostframecallback/). – Xavi Baz Apr 06 '20 at 22:22
  • @StoychoAndreev is there any method to set snackbar message on first page and show it on second page after redirection from first page to second page? Kindly suggest. Thanks. – Kamlesh May 15 '21 at 07:36
7

You have to return a result to the previous page which will represent the action shown on the page.

In the 1st page When you are navigating to the page change a few things.

bool result=await Navigator.of(context).push(/*Wherever you want*/);

Then in the second page when you are returning to the previous page send some result to the 1st page.

Navigator.of(context).pop(true);

if the work is not success you can return false.

You can return any type of object as result

Then In the 1st page you can check for the result and show the snackBar accordingly

bool result=await Navigator.of(context).push(/*Wherever you want*/);
if(result!=null && result==true){
    //Show SnackBar
}else{
    //Some other action if your work is not done
}
Guru Prasad mohapatra
  • 1,809
  • 13
  • 19
3

Since showing SackBar require an active Scaffold, you will need to pass the message to the new route page something like getSavedThingRoute(message: 'You saved the thing!') and the new page is responsible for displaying the message.

Typically I happen to use Navigation.pop({'message': 'You saved the thing!', 'error': false}) to pass the message.

Kyaw Tun
  • 12,447
  • 10
  • 56
  • 83
  • 1
    here are how you do this https://flutter.dev/docs/cookbook/navigation/returning-data – buncis Apr 23 '19 at 12:56
  • 1
    The context changes and my app crashes even though I followed the full documentation steps! – Samer May 10 '19 at 18:20
2

In your 2nd Page's class, override the initState method

@override
void initState() {
  super.initState();
  
  // Shows the SnackBar as soon as this page is opened. 
  Future(() {
    final snackBar = SnackBar(content: Text('Hello World'));
    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  });
}
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
  • it's second part of solution, could you please suggest me how did you set snackbar message on 1st page? Thanks. – Kamlesh May 15 '21 at 07:29
  • 2
    Thanks, I have solved my problem. Just set snackbar message on firstpage and navigate to second page. I can see snackbar message on second page, was set on first page. Thanks again. – Kamlesh May 15 '21 at 08:46
1

In case anyone using Flushbar plugin. Put this Inside your build function before return statement.

WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
 
  Flushbar(
    message: "Message from the top",
    flushbarPosition: FlushbarPosition.TOP,
    icon: Icon(
      Icons.info_outline,
      size: 28.0,
      color: Color(0xff00d3fe),
    ),
    flushbarStyle: FlushbarStyle.FLOATING,
    duration: Duration(seconds: 5),
  )..show(_scaffoldKey.currentState.context);
});}

of course don't forget the scaffold key

final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
Jiten Basnet
  • 1,623
  • 15
  • 31
0

In my case the reason for the snackbar not being shown was the floating action button , I had a logic to put the fab or not and when there was no fab the snack bar did not show , I did not find a proper solution for this but putting a fab like this solved my problem . however putting Container() , null will cause problems.

 Visibility(
          visible: false,
          child: FloatingActionButton(
              onPressed: () {},
            ),
        );
0

var msg = ScaffoldMessenger.of(context) .showSnackBar( SnackBar( content: Text('Processing Data'), ), ); msg.closed.then( (value) => Navigator.pushNamed( context, '2'), );

Janvi
  • 1
0

For anyone having this issue in 2023, after scaffoldKey.currentState.showSnackBar() (where scaffoldKey = GlobalKey<ScaffoldState>()) got deprecated, and Flutter introduced

ScaffoldMessenger.of(context).showSnackBar(Snackbar(content: Text('')))

which supports displaying the Snackbar on every screen even after navigation, make sure the screen you're navigating to has a Scaffold. My problem was that the screen I was returning to didn't had a scaffold. It was just returning the Material() widget as it was the Splash screen of the application.

You can also use a global key if necessary, like so:

final _scaffoldKey = GlobalKey<ScaffoldMessengerState>();

then using it as mentioned in previous comments

_scaffoldKey.currentState.showSnackbar();

as shown in the official documentation or this official video