This is my second page button, go to page 3.
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder:
(context) => MapsPage(index: widget.index))
);
},
This is the page 3 button that will send information back to page 2, but when the second page is pressed to go back to page 1, it has to be pressed twice. It's like replacing a new face now.
void saveAddress() {
var lat = centerMap.latitude;
var lng = centerMap.longitude;
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (_) => EditAddressPage(
index: widget.index,
lat: lat.toString(),
lng: lng.toString(),
),
));
print("Saving address: Latitude: $lat, Longitude: $lng");
}
The pushReplacement method replaces the current route on the navigation stack with a new route, rather than pushing a new route on top of the stack. This means that when you press the back button, you are not going back to the previous route (Page 2), but instead going back to the route that was replaced (Page 1).
To fix this issue, you can use the push method instead of pushReplacement when navigating from Page 3 to Page 2. This will add a new route to the navigation stack, rather than replacing the current route, and allow you to navigate back to Page 2 when the back button is pressed.
Related
In my app, I have a homepage that has 5 tabs on the bottom. On each tabbed page, there is an app bar that has a '+' symbol as an action, which navigates you to a different page. The navigation with that '+' button to the new page is done with the following code, alongside the Flutter Platform Widgets package:
Navigator.of(context, rootNavigator: true)
.push(
platformPageRoute(
context: context,
builder: (context) => Page1(),
),
);
I use the platformPageRoute feature as an easy way to navigate with a native feel. Now, that works fine to navigate to a new page, but the issue comes when I use
Navigator.pop(context);
to navigate back to the original page. When I use that to navigate back to that original page, it pays no attention to the tab that was selected originally. For example, if I were originally on the second tab on the homepage and then use the '+' button on that tab and then finally use
Navigator.pop(context);
on that new page, it returns the first tab of the homepage. Is there any way of ensuring when I use the above command, it goes to the right tab? I have tried something along the lines of:
Navigator.popUntil(context, '/homepageTab2');
alongside a named route, to return to the correct tab on the homepage, although that returns a black screen. Why might that be? I have also tried using:
Navigator.pushAndRemoveUntil(
context,
platformPageRoute(
context: context,
builder: (context) =>
HomePage(selectedPage: 1),
),
(route) => false,
);
This does not work either, since it returns the selected/correct page tab content, but with the first tab selected. In addition, the other
'problem' for me is that the animation is a 'push' one and that doesn't 'match' with the animation when I have more often used
Navigator.pop(context);
to navigate back to a screen. Is there a way to maybe use pushAndRemoveUntil but then change the animation to match a pop animation?
Thanks!
EDIT:
I have just noticed that with the situation I have described above, it is actually returning the correct screen content when I use Navigator.pop(context); but the tab in the tab bar at the bottom is showing as the first tab, in the second tab's position, essentially duplicating the first tab, until I navigate to a new tab and back, at which time it shows the correct tab in the correct position. I hope that makes sense!
As it turns out, the issue wasn't related to Navigator.pop(context); being used. It was the way I was controlling the selected tab. I'm posting this as an answer incase it helps someone else.
Initially, I created late values for a tab controller and the current selected page, like so:
late TabController _tabController;
late ScrollController _scrollController;
late int _selectedPage;
Then, I created a list of widgets that represented the actual page to display for each selected tab:
List<Widget> _pageWidgets = <Widget>[
Page1();
Page2();
Page3();
Page4();
Page5();
];
Then (and I think this was the bit that wasn't working) I used initState() as follows:
void initState() {
super.initState();
// Initialising a value that allows the 'final' page selector to be changed
_selectedPage = widget.selectedPage;
// Initialising the tab controller
_tabController = TabController(
length: 5,
vsync: this,
initialIndex: _selectedPage,
);
// updating the tab index when a new item is selected
_tabController.addListener(() {
setState(() {
_selectedPage = _tabController.index;
//_tabIndex = _tabController.index;
});
});
// Creating the scroll controller
_scrollViewController = ScrollController();
// Scrolling view to top when a new tab is selected
_tabController.addListener(() {
setState(() {
_scrollViewController
.jumpTo(_scrollViewController.position.minScrollExtent);
});
});
}
I then controlled the page content like this:
body: _pageWidgets.elementAt(_selectedPage),
I'm not 100% sure why this wasn't working, although I believe it would have something to do with the fact that initState() would only be called during the build and therefore placing the functionality inside there would mean changes wouldn't be detected. Either way, my new method, which works perfectly, is:
/// Controls the screen to display first
int _index = 0;
/// Creating a navigation key to control tab bar navigation
final _navigationKey = GlobalKey<CurvedNavigationBarState>();
Then, within the Scaffold() I show the page content like this:
body: _pageWidgets.elementAt(_index),
And finally, within the navigation bar (which is the CurvedNavigationBar() package from pub.dev) I give it a key and the index:
key: _navigationKey,
index: _index,
And this controls it perfectly, showing the correct tab.
Sub-pages of a TabBarView cannot be navigated using Navigator.
You can use TabController to go to your desired tab page after awaiting Navigator.push():
await Navigator.of(context, rootNavigator: true)
.push(
platformPageRoute(
context: context,
builder: (context) => Page1(),
),
);
tabController.animateTo(<index of tab>);
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
In my project I move from the current page (2) to the previous one (1) an updated a List, through the constructor, with the back button in the appBar
onPressed: () => Navigator.pushReplacement (
context,
new MaterialPageRoute (
builder: (context) => new PagesListPage (
pagesList: list Pages,
)),
)
The data is updated correctly on the previous page
but Flutter rightly 'duplicates' the previous page (1) in the stack, that is:
on page 1 - tap on button to go to page 2 ---> I see page 2
on page 2 - tap on button back in appBar (navigator.pushReplacement) ---> I see page 1
on page 1 - tap on button back in appBar ---> I see again page 1
How can I eliminate the double occurrence of page 1 in the stack?
Or alternatives to update the List on page 1 from page 2.
I'm starting out with Flutter, and I've also tried with Provider, but if I understand correctly it can only update a 'value', I couldn't get it to update a 'List'!
Thanks for any valuable suggestions
On page 1 navigator:
buttonPressPage1() async {
var updatedData = await Navigator.pushReplacement(
context, new MaterialPageRoute(builder: (context) => page1));
setState(() {
oldData = updatedData;
});
}
On page 2 navigator:
buttonPressPage2() {
Navigator.of(context).pop(updatedData);
}
Using await to you can update the data.
I want my flutter app to navigate from my last screen back to the home page. Currently, it navigates back to the previous page which I do not want.
onPressed: () {
Navigator.pop(context, MaterialPageRoute(builder: (context) => MyHomePage()));
},
I expect the button to return me back to the Home Page
If you don't use named routes you can simply replace your code with this:
onPressed: () {
Navigator.of(context).popUntil((route) => route.isFirst);
},
This will pop the navigation stack until it reaches the first item which might be your home page.
Can't you just use Navigator.pushAndReplace(MaterialPageRoute(builder: (context) => MyHomePage())); for navigation back to the home page?
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.
});
});