How can I go back to recent page of history with onPopPage in navigation 2.0 - flutter

I'm practicing the new API, navigation 2.0, for my web project with flutter. I'm quite new to flutter either.
I successfully implemented the routing with navigation 2.0, but still can't figure out how the onPopPage works. If I tapped the button to navigate to the sub-page, the back button is automatically generated in the header. So when I clicked the back button in the header, it takes me home.
What if I implement another nested sub-page of the sub page, then I clicked the back button it takes me all the way to the top page, home.
I think it's because of the onPopPage function which I added like this. How can I edit this function to work properly. I want the back button to pop the only recent page of navigation history.
onPopPage: (route, result) {
if (!route.didPop(result)) {
return false;
}
_currentPath = 'home';
show404 = false;
notifyListeners();
return true;
},

Related

Flutter remove specific route from route stack

I want to remove a specific route and stay on the same page without navigating to different page.
I have three pages PageA, PageB, PageC. I have the below code in PageC and removing PageB from Route stack. After removing it's navigating back to PageA (default route page). I want to stay on the RouteC without navigating.
final newRouteName = "/PageB";
navigatorKey.currentState!.popUntil((route) {
if (widget.routeSettings!.name == newRouteName) {
navigatorKey.currentState!.removeRoute(route);
}
return true;
});
Please help me, i stuck with this for days
I dont know if this works for you, but probably what you can do is:
When navigating from 'Page B' to 'Page C' make a
void _moveToPageC() {
Navigator.pushReplacementNamed(context, '/routeC');
}
That way, your stack would be page a > page c
Then if you want to go back to page a then you can simply pop page C
Hope, this helps

Flutter - re-run previous page code after execution returns to it

I'm trying to figure out the best way to implement the proper navigation flow of a flutter app I'm building that involves a 3rd party authentication page (Azure AD B2C). Currently I have a page that serves simply as a "navigate to 3rd party auth login" page which is set as the initialRoute for my app. The first time through, it runs exactly the way I want it to, but I'm not able to figure out how to get that 'navigate to auth' page to re-run when navigated back to (after logout) so that the user ends up back at the 3rd party auth login page.
Basically what I'd like to do is, on logout - have the app navigate back to that page specified as the initialRoute page, and upon that page being navigated back to, have it re-launch the 3rd party auth login page just like it did the first time it executed.
I tried just awaiting the call to Navigator.push() and then calling setState((){}) afterwards, and that does re-display the page, but it just leaves that initial page sitting there, and doesn't end up triggering the execution the way it did the first time. initState() does not fire again, so neither does any of my code that's in there.
I've tried various methods off the Navigator object trying to reload the page or navigate to itself again, or just calling goToLogin() again after the await Navigator.push() call, nothing works.
Here's what I'm currently doing :
User launches the app, the initialRoute is LoginRedirect
class LoginRedirect extends StatefulWidget {
#override
_LoginRedirectState createState() => _LoginRedirectState();
}
class _LoginRedirectState extends State<LoginRedirect> {
#override
void initState() {
Utility.getConfig().then((value) {
config = value;
oauth = AadOAuth(config);
goToLogin(context);
});
super.initState();
}
void goToLogin(BuildContext context) async {
setState(() {
loading = true;
});
try {
await oauth.login(); // this launches the 3rd party auth screen which returns here after user signs in
String accessToken = await oauth.getAccessToken();
navigateToDashboard();
setState(() {
loading = false;
});
} on Exception catch (error) {
setState(() {
loading = false;
});
}
}
void navigateToDashboard() {
await navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => Dashboard()));
// right here is where I'd like to call goToLogin() again after I Navigator.popUntil() back to this
// page, but if I try that I get an error page about how 'The specified child already
// has a parent. You must call removeView() on the child's parent first., java.lang
// .IllegalStateException and something about the bottom overflowed by 1063 pixels
}
}
After getting some config values and calling oauth.login() then I call a navigateToDashboard() method that pushes the Dashboard page on to the navigation stack.
Elsewhere in the code I have a logout button that ends up calling this code:
oauth.logout();
Navigator.popUntil(context, ModalRoute.withName('/LoginRedirect'));
which returns execution to where I called await Navigator.push() previously. But I can't figure out what I need to do there to have that LoginRedirect page execute again. I can't call goToLogin() again or it errors/crashes. I can't call initState() again, calling setState() doesn't do anything. I'm kinda stumped here, I thought this would be easy.
When logging out try: Navigator.pushReplacementNamed(context, "/LoginRedirect"); instead of Navigator.popUntil(context, ModalRoute.withName('/LoginRedirect'));

Flutter - navigate back to specific tab on a page with Navigator.pop(context)

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

Flutter: conditional bottom navigation bar, to show pages based on if condition

I have implemented a bottom navigation bar with the logic listed below, and I want to check if the user is logged in. If user is logged in on third tab I want to show the profile page instead of SignIn page. This is my code where I am displaying the bottom navigation bar, I am stuck on what to do after checkIsLoggedIn() async function.
After that Im building list of widgets to show the pages in 3 tabs.
code of which is below,
so in place of SignIn(), I need to show, if the user is logged in it shows signin other wise, it shows Profile page.
Please do help me out here.
This may help.
Future<void> checkIfLoggedIn() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var id = localStorage.getString('id');
if (id != null) {
setState(() {
_children[2] = ProfilePage();
});
}
}
Inside checkIfLoggedIn(), along with _isLoggedIn write the following code: _children[2] = Profile().
You can set the last BottomNavigationItem as below
BottomNavigationItem(
icon:_isLoggedIn ? Icons.person : Icons.key,
title:Text(_isLoggedIn ? 'Account' : 'Log in' )
)
and you can handle the click event and set the proper page in the build() by the same ternary operators using the _isLoggedIn variable
build(){
return Scaffold(
body:_isLoggedIn?ProfilePage():LoginPage()
);
}

Flutter Navigation showing route as /

I have a card with PopupMenuButton that I can tap to perform the 'copy' action. I can also perform the same action from the card details page. The problem I have is to get back to the home page I have to cater for both paths. So if the action is performed from the details page, I need to pop twice to go back to the home page.
Action from the home page:
Home > add card page
navigator.pop() takes me back to home page
Action from the details page:
home > card details page > add card page
Having to pop twice here.
I know I have to do the pop atleast once and then I used the popUntil to check the current state of the route. For some reason, it is always popping twice.
After debugging, its showing current route as /
void _changeRoute(String newRouteName) {
bool isNewRoute = false;
// Pop once
Navigator.pop(context); // after this I should be on home page for the first path
Navigator.popUntil(context, (route) {
print('Current route is ${route.settings.name}');
if (route.settings.name == newRouteName) { // but this is showing current route as /
isNewRoute = true;
}
return true;
});
print('is home page: $isNewRoute');
if (!isNewRoute) {
Navigator.pop(context);
}
}
I have this working.
My problem was that in my Main App, I had specified the home property on MaterialApp. According to the documentation, if the home property is specified, it takes the route /. So my home page is / and I had it defined as /home in my routes and this was being overridden by /.