Show a snackbar after navigate in Flutter - flutter

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?

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

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
}

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.

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

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

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: () {},
),
);

Related

Flutter: push a new page and scroll down to certain widget

I have a web app in Flutter. My app bar has 5 buttons. Four of them scroll within the first page. The 5th one pushes a new page.
There are three different widgets on stage:
page A (HomeView)
page B (ContactView)
the AppBar
When the user is on page A, the buttons of page A should scroll down or up, and the button of page B should push the new view. When the user is on page B (contact) and presses any of the buttons of page A, it should push the home page and scroll to the required position.
I tried to do this with ScrollController.animateTo and Riverpod for state management, but I couldn't do it. The page scrolled down but, for some reason, it scrolled up again and didn't maintain position. I couldn't find any related post on the Internet and keepScrollOffset was not the answer. I also didn't know how to scroll to the desired position when the user is not on 0 (it scrolled down from current position).
After that, I tried to do it with GlobalKeys. It worked, up to a certain point. On page A, everything worked fine. However, from page B to page A, it said the keys are duplicated. I used pop instead of pushNamed as suggested in one of the answers of this post (the only answer that worked for me). The behavior was finally as expected, but I can't use pop since I have more pages on the website, so I can't ensure that the user is coming from page A. I tried with pushReplacement as the same answer gives that for an alternative option. It doesn't work now. When trying to go from page B (contact) to page A, it throws error Unexpected null value.. Furthermore, I need to keep the back button functionality since it is a website. I would prefer, then, to not pop the current page from the stack.
The page A (HomeView) is built like this inside of a StatefulWidget:
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
controller: _scrollController,
child: Column(
children: [
HomeHeader(key: HomeView.homeKey),
WhatWeOffer(key: HomeView.servicesKey),
WhatWeDo(key: HomeView.referencesKey),
WhoWeAre(key: HomeView.aboutKey),
const LetsGetInTouch(),
],
),
);
}
The GlobalKeys are defined like this inside of the Page A (HomeView) widget:
static GlobalKey homeKey = GlobalKey(debugLabel: 'homeHeaderKey');
static GlobalKey servicesKey = GlobalKey(debugLabel: 'whatWeOfferKey');
static GlobalKey referencesKey = GlobalKey(debugLabel: 'whatWeDoKey');
static GlobalKey aboutKey = GlobalKey(debugLabel: 'whoWeAreKey');
This is how I implement the navigation to page A:
if (ModalRoute.of(context)?.settings.name != '/') {
Navigator.pushReplacementNamed(
context,
Navigation(context).routes["/"]!,
);
}
Scrollable.ensureVisible(
HomeView.homeKey.currentContext!,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
And this is how I implement the navigation to page B:
Navigator.pushReplacementNamed(
context,
Navigation(context).routes["/contact"]!,
),
Maybe GlobalKey is not the right approach to my problem? Is there any easier way to push a new page and scroll down to a widget?
EDIT:
I think the Unexpected null value problem was caused because the view was not built yet when I was trying to call it. I solved it by making the functions async and waiting before using the keys:
TextButton(
child: Text('home', style: style),
onPressed: () async {
if (ModalRoute.of(context)?.settings.name != '/') {
Navigator.pushReplacementNamed(
context,
Navigation(context).routes['/']!,
);
await Future.delayed(const Duration(milliseconds: 500));
}
Scrollable.ensureVisible(
HomeView.homeKey.currentContext!,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
),
However, this does not solve my problem of the back button. As I use pushReplacementNamed instead of pushNamed, I can't go back. Is there a more optimal way to do this, where I can keep the back button functionality?

How to route file to a different page in flutter

I'm making an app with Dart/ flutter now and having an issue with routing pages.
The app is basically the matching app, and I want to add the functionality that a user can edit their profile. Currently, we have 4 stateful widgets: Match(), Message(), MyProfile() and EditProfile(). In the bottom navigation bar, I put three widgets, Match(), Message(), and MyProfile(); when the user wants to change the profile information, the person goes to MyProfile() and click "edit profile" button, which takes the user to EditProfile. After the user changes the information, I want to rout the page to MyProfile() for allow the user to check the profile info.
The code below is some part of the Navigation bar.
class _NavigationHomeState extends State<NavigationHome> {
int _currentIndex = 0;
final List<Widget> _children = [
Match(),
Messages(),
MyProfile(),
];
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
and I put the code below in one of the button of the EditProfile().
Navigator.of(context).pushNamed('/myProfile');
Then gave me an error saying
Error: Could not find the correct Provider above this
MyProfile Widget
This likely happens because you used a BuildContext that does not
include the provider of your choice. There are a few common scenarios:
The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route,
then other routes will not be able to access that provider.
You used a BuildContext that is an ancestor of the provider you are trying to read.
Make sure that MyProfile is under your
MultiProvider/Provider. This usually happen when you are
creating a provider and trying to read it immediatly.
I guess I'm getting this error because we set the _currentIndex in the bottom navigation bar as 0, which is Match(), so Navigator.of(context).pushNamed('/myProfile'); trying to take the user to the Match() page (?)
How can we take the user to the MyProfile page after the person save and click the button on EditProfile()?
Try using MaterialPageRoute instead of named routes.
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
}
If you going to EditProfile from MyProfile widget then I suggest to just pop the screen. As this will not reload complete screen, you can always pass data while doing pop.
RaisedButton(
onPressed: () {
// The Yep button returns "Yep!" as the result.
Navigator.pop(context, 'Yep!');
},
child: Text('Yep!'),
);
Another option is to use pushNamedAndRemoveUntil method.
navigator.pushNamedAndRemoveUntil('/MyProfile', ModalRoute.withName('/MyProfile'));
I guess I'm getting this error because we set the _currentIndex in the
bottom navigation bar as 0, which is Match(), so
Navigator.of(context).pushNamed('/myProfile'); trying to take the user
to the Match() page (?)
Not sure about your error but you can pass data as argument while doing pushNamed.
RaisedButton(
child: Text("Navigate to MyProfile"),
onPressed: () {
Navigator.pushNamed(
context,
'/myProfile',
arguments: [
'Extract Arguments Screen',
'This message is extracted in the build method.',
],
);
},
),

Flutter - set result to return when the user navigates back

When calling Navigator.pop() a result can be passed to the previous screen. Is there a way to set the result so that if the user navigates back on their own the result is still returned to the previous page?
I could pass an object to the second page and then modify it so that the first page can check it when the second page returns, but I'd rather use the result returned from the Navigator as it's more readable.
Overriding the back button's tap detector as I've seen suggested elsewhere is not an acceptable solution because the user may navigate back in some other way such as swiping or pressing the Android back button.
Yes, you can pass data between screens both ways.
While popping back, send the data you wish to send like this from the second screen
RaisedButton(
onPressed: () {
// The Nope button returns "data" as the result.
Navigator.pop(context, 'data');
},
child: Text('Nope!'),
);
And catch the result in the your first screen like this
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
source
For the other concern where the user is able to go back to the previous screen by pressing the back button, you can wrap your second screen with WillPopScope widget and provide the onWillPop callback. This will override the popping of the second screen and execute the callback where you can define the value you wish to return from the second screen.
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(), // or your widget
onWillPop: () {
return Future.delayed(Duration(microseconds: 0), () {
return Navigator.pop(context, "return-data");
});
},
);
}
Yes, this is possible, when navigating to Screen B from Screen A, make the onTap() functionasynchronous and await the result from Screen B.
Check the code below: It works perfectly:
On Screen A, put this code:
onTap: () async {
// receive the data you are sending from screen B here
final result = await Navigator.push( context);
},
On Screen B, put this code:
onTap: (){
// pass the data you want to use in screen A as the second paramter
Navigator.pop(context,number);
},
I hope this helps

Trying to display a Flutter snackbar throws an exception error

Please before mark it as duplicate take a look, I have been trying this thread but i can't make it work.
I am trying to make my learning app to display a snackbar, and yes i ran the google cookbook sample and it works, but i am trying to challenge my self doing something else, basically what it is, is just a scaffold with two buttons that increase and decrease, when the index is zero i want to display a snackbar that index is zero, can't decrease more than that. So i got this button and onPressed method calls _onTapPrevious method which passed the context parameter, but i am not sure this is the correct way to pass the context.
new RaisedButton(
padding: const EdgeInsets.all(8.0),
textColor: Colors.white,
color: Colors.redAccent,
onPressed: () => _onTapPrevious(context),
child: new Text("Previous"),
),
Here is the _onTapPrevious method that i created a snackbar, and also checks whether the widget index is > than 0 and if so it can decrease the index, and when it reaches 0 then it should display the snackbar.
_onTapPrevious(BuildContext context) {
final snackBar = SnackBar(content: Text("Index 0, can't decrease more."));
setState(() {
if (widget.index > 0) {
widget.index--;
} else {
Scaffold.of(context).showSnackBar(snackBar);
}
});
}
The error that i get:
flutter: Another exception was thrown: Scaffold.of() called with a context that does not contain a Scaffold.
try to add
final _scaffoldKey = GlobalKey<ScaffoldState>();
to your class, then in Scaffold:
key: _scaffoldKey,
and finally:
_scaffoldKey.currentState.showSnackBar(snackBar);
Hope that helps!

which widget can be used to explain functionality in app

I want to explain something in my app and add a widget which looks like a notification or chat. I want this widget to be visible for some time and then get dismissed. I tried using tooltip but it is visible only when I click it.
Which widget can I use?
The Dart package intro_views_flutter is what you need, but one of its main limitations is that it is displayed on full screen, if that is not an issue to you, then you should take a look at it. Or you can use a showDialog method inside a Future function this way :
Future showNotification() async {
showDialog<String>(
context: context,
child: new AlertDialog(
title: Text('Note!') ,
contentPadding: const EdgeInsets.all(16.0),
content: //any widget you want to display here
),
);
await new Future.delayed(const Duration(seconds: 5), () {
Navigator.of(context).pop(); // this will dismiss the dialog automatically after five seconds
}
}
then when you need it call:
showNotificaion();