How to update widgets when a route resumes? - flutter

Is there any equivalent for Android Acyivity's "onResume" in flutter? I want to refresh a widget in a previous route when Navigator pops a route and comes back to previous route. WidgetBingingObserver only works when app is resumed from background not when a route is resumed.

When you push to new page, you will get the Future which will get resolved on pop.
var future = Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
future.then((value) {
print(value);
print("popeed");
});
You can send values from nextPage like (It is optional, if you are not sending it will be null while resolving future)
Navigator.pop(context, 2);

Related

How to get returned value from Navigator.pop using Navigator.restorable?

Currently my original way of getting values between screen using Navigator.pop was like this:
Second screen goes back to home screen and passes a boolean:
Navigator.pop(context, false);
Home screen awaiting for the Navigator and capturing the value:
var bookmarked = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const CorePage()),
);
However I've recently started using restorable to restore states when the app randomly closes such as:
static Route<void> _staticGoToCoreWorkout(context, arguments) =>
MaterialPageRoute(builder: (context) => const CorePage());
Navigator.restorablePush(context, _staticGoToCoreWorkout);
The problem I'm having is that something like this doesn't work anymore:
var bookmarked = Navigator.restorablePush(context, _staticGoToCoreWorkout);
How can I go about getting the boolean value (or any value really) that's passed from the Navigator.pop back to the previous screen when I'm using restorable?
UPDATED:
Adding await doesn't yield results either. It will always run pass the code.
var bookmarked = await Navigator.restorablePush(context, _staticGoToCoreWorkout);

Flutter get reference after navigation pop

I would like to get a reference to the screen I am going to show.
With this code:
Navigator.of(context).pop()
I actually going back to the previous screen but I would like to access that reference to do some work.
How can I do that?
With my iOS background working in Swift I would have done something like that:
var newScreen = Navigator.of(context).pop();
You cannot create a reference because when you pop the current "route" all the data will be disposed.
But you can return a pop result to the awaiting parent as explained by official docs.
Something like:
//When you will dispose the pushed page/route you can pass your result to pop method
Navigator.pop(context, 'This is my new page result');
//In the parent page you will await for the result
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyPushedPage()),
);
print(result) //--> This is my new page result

How to call a function to update a value after popping a screen in Flutter?

Screen 1: shows list of item with add button.
Screen 2: form to add a new item to the list.
Screen 2 >> Screen 1 - While calling navigator.pop() in screen 2, how to call method/setState (to update list) in screen 1?
Can anyone help me out?
I don't want to relaunch screen again. I just need to run a method to update my list after popping previous screen?
When you navigate from Screen 1 to Screen 2, you use the push method. The push method returns a Future object.
You can use the then method of Future to execute your code after Screen 2 was popped from the navigation stack.
Navigator.of(context)
.push(MaterialPageRoute(
builder: (context) => Screen2(),
))
.then((value) {
// you can do what you need here
// setState etc.
});
You can use Provider or BLoc or you can await for the result when you push the page
You can do it like this :
// this method waits for the pop to be called
var data = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
);
debugPrint(data);
// here the second argument is the data
Navigator.pop(context, "data"); // popped from LoginScreen().
output
data
Same method can also be done like below
// this method waits for the pop to be called
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
).then((data){
// then will return value when the loginScreen's pop is called.
debugPrint(data);
});
Here is a good article to look into this
https://medium.com/flutter-community/flutter-push-pop-push-1bb718b13c31
Navigator.pop has a second argument called result.
You could then return the value that you need to the first page that will read it as the return value of Navigator.push

Flutter: Move to a new screen without providing navigate back to previous screen

I'm implementing an authentication flow in my Flutter app.
After a sign in attempt, the CheckAuth (which checks whether a user is signed in or not and then opens home screen or sign up screen accordingly) is opened with this code:
void _signIn() async {
await _auth
.signInWithEmailAndPassword(
email: _userEmail.trim(), password: _userPassword.trim())
.then((task) {
// go to home screen
if (task.getIdToken() != null) {
setState(() {
Navigator.pushReplacement(
context,
new MaterialPageRoute(
builder: (BuildContext context) => new CheckAuth()));
});
} else {
print("Authentication failed");
}
});
}
Problem: I can successfully sign in to the app, but if I tap back button after I sign in, it goes back to the sign in screen (while I expect it to exit from the app).
Question: How to move from one screen to another in Flutter without the way back?
Do I need to somehow delete the navigator history? Or don't use navigator at all? I tried Navigator.replace method, but it didn't seem to work.
You need to use Navigator.pushReplacement when leaving the auth screen too. Not just when redirecting to login page.
You can use the pushAndRemoveUntil method:
Push the given route onto the navigator that most tightly encloses the given context, and then remove all the previous routes until the predicate returns true. To remove all the routes below the pushed route, use a [RoutePredicate] that always returns false (e.g. (Route<dynamic> route) => false).
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => MainPage()),
(Route<dynamic> route) => false,
);
You need to use
Navigator
.of(_context)
.pushReplacement(new MaterialPageRoute(builder: (BuildContext context) => page));
Where _context is object of BuildContext
And page is which page you directed to.
I think you probably have already solved this. But you can set "automaticallyLeadingImplied: false" in the AppBar of the Scaffold you are navigating to.
Just simply add the below code:
Navigator.of(context).pushNamedAndRemoveUntil('/routeName', (route) => false);
We can use routes for the same
Like:
routes: {
LoginScreen.route_name: (_) => LoginScreen(),
.....
},
And use below code whenever you want to push and remove the backstack
Navigator.of(context).pushReplacementNamed(LoginScreen.route_name);
Note: Define static String inside widget LoginScreen
I have resolved this by popping from current page and showing new page:
Navigator.pop(context);
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context) => newPage));
Here is the solution -
Use pushAndRemoveUntil instead of pushReplacement
Also, can be use maintainState: true
For set root page
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => pageName, maintainState: true),
(Route<dynamic> route) => false);
For Push Page one page to another
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => pageName,
maintainState: false),)
**If you want to refresh always while appearing page then use: **
maintainState: false
For anyone wondering there is a new argument that needs to be returned false Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false);
Navigator.of(context, rootNavigator: true).pop();
Navigator.pushNamed(context, '/');
It was not working for me because I was using the home proptery rather than initialRoute on the MaterialApp.
This is a link where there's the folloing warning which helped me to spot the error: https://docs.flutter.dev/cookbook/navigation/named-routes#2-define-the-routes
Warning: When using initialRoute, don’t define a home property.
if you are working with Getx state managemaent then you can try this
Get.of(()=> NewPage());

How to refresh a page after back button pressed

I have a "list page". and an add page. User clicks "add page", goes to a new page where they add some entity. Then I pop the Navigator, and user goes back to "list page".
At this point, I want to get notified somehow - callback maybe? override a method, I am not really sure - and re-read the information from database.
Is there a hook like this in Flutter? Or is there another pattern that I should use?
The Navigator.push method returns a future of the generic type of the Route:
Navigator.of(context)
.push(new MaterialPageRoute<String>(...))
.then((String value) {
print(value);
});
And inside the new route, when you have the value you need:
Navigator.of(context).pop('String');
If at your list page, you create function let say retrieveData() to read list of the data then call it inside initState, if you're using pushNamed route, the simple solution would be
Navigator.pushNamed(context, '/adddatapage').whenComplete(retrieveData());
If you're using push route,
Navigator.of(context).push(new MaterialPageRoute(builder: (context) => AddPage())).whenComplete(retrieveData);
That should be enough. No need to additional code on the add page. This will be works if user press back button also, again, without additional code in the add page.
Adding a option to #ian 's answer
Navigator.of(context)
.push(new MaterialPageRoute<String>(...))
.then((value) => setState(() => {}));
This worked for me. This will rebuild whenever page is loaded.
Write below code when redirect to screen 2,
Navigator.pushNamed(context, '/screen2').then((_) {
// This block runs when you have returned back from screen 2.
setState(() {
// code here to refresh data
});
});
Use
Navigator.pop(context);
in screen 2 to back to screen 1
i use Navigator.pushAndRemoveUntil, it will refresh the page and load the data again, this is example :
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) =>
CustomerMainPage()),
(Route<dynamic> route) =>
false);
I passed a callback method into the "add" page as a Navigator route argument. The callback is a method in my list page. The "add" page executes the callback when exiting. The callback method then simply refreshes the data and calls setState to refresh the screen.
My architecture is using Provider and viewmodels, but it should be straight-forward in a standard app as well.
For named route:
Navigator.pushNamed(context, '/page2').then((_) {
// This block runs when you have returned back to the 1st Page from 2nd.
setState(() {
// Call setState to refresh the page.
});
});
For unnamed route:
Navigator.push(context, MaterialPageRoute(builder: (_) => Page2())).then((_) {
// This block runs when you have come back to the 1st Page from 2nd.
setState(() {
// Call setState to refresh the page.
});
});