Navigate to inner page when on another page - flutter

How can I navigate to an inner page of a PageView when I'm on another page. For example: say I have the following pages:
Home
All Messages > Single Message Page
Contact
When I'm on the Contact page, how can I go straight to the Single Message Page? I feel it's easiest to use named routing, so would love to have sucha sollution....

As you mentioned in your own question, named routing would be the right way to do so.
With named routing, the route tree is much easier to understand and move around.
For Example, I defined in my main app widget, the exact routes in my app.
As you can see from the Flutter documentation:
MaterialApp(
// Start the app with the "/" named route. In this case, the app starts
// on the FirstScreen widget.
initialRoute: '/',
routes: {
// When navigating to the "/" route, build the FirstScreen widget.
'/': (context) => FirstScreen(),
// When navigating to the "/second" route, build the SecondScreen widget.
'/second': (context) => SecondScreen(),
},
);
After you have defined the routes, you can move to this route by pushing a named route to the Navigator of your app.
// Within the `FirstScreen` widget
onPressed: () {
// Navigate to the second screen using a named route.
Navigator.pushNamed(context, '/second');
}
Read more at https://flutter.dev/docs/cookbook/navigation/named-routes
Wish you luck!

Related

Keep Navigator state data (provider/InheritedWidget) when pushing routes

In my flutter app I have a list of instances, when user clicks one I open the instance page and provide the InstanceData model to it using
Navigator.push(context, MaterialPageRoute(builder: (context) =>
ChangeNotifierProvider(
create: (_) => InstanceData(...),
child: Instance()
)
));
This does work, but when the Instance widget does Navigator.push, the provider data is lost because my provider is below the app's Navigator.
I googled for a solution and found that I can do:
Navigator.push(context, MaterialPageRoute(builder: (context) =>
ChangeNotifierProvider(
create: (_) => InstanceData(),
child: Navigator(
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (context) => Instance()
);
},
)
)
));
Here's the dartpad to show it: https://dartpad.dev/?id=51fcbabb1ad401afb193558ccde3b5e1
Now, all (even deeply nested) widgets that are opened from Instance() will have access to the InstanceData. Also, I actually need multiple nesting. E.g. we click Instance and create Provider for it, then inside Instance we click Area and I want all widgets from Area() and below to have access to both InstanceData and AreaData, so I will need to create another Navigator to hold AreaData. Think of it as in this example:
https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf
which shows that we keep separate history inside each Tab. Now, if each my Instance is in its own Tab, and Instance also has tabs panel with each tab being Area that also has to keep its own history - that's what I need.
However I am new to flutter and I am not sure if that's the correct approach. Creating own Navigator on each route push just so that my Inherited/Provider data is accessible? Looks like a hack to me.
E.g. what if I want to show two Instance() widgets side by side - I would wrap them with their own Navigator just to pass the data but isn't navigator supposed to track pages/screens instead? How will it behave with the Back button?
So can somehow confirm the solution and if not, them suggest a state management for flutter that will allow to create do something like
Navigator.push(context, MaterialPageRoute(builder: (context) =>
CoolProvider(
create: (_) => InstanceData(...),
child: Instance()
)
));
where CoolProvider does work for any Navigator.push() inside any child widget of the Instance(), even if that's some "CoolNavigator.push" instead. Maybe it's some kind of sub-routes? E.g. Navigator.pushSubRoute() so that the main route is still there?
Maybe it's not even about Navigator? E.g. in the side-by-side Instances having Navigator is almost meaningless - user can't navigate to both instances at once, but I do need each Instance() to be clickable and to go to nested Areas with the InstanceData() available to those widgets.
Update: In fact I tried it side by side here:
https://dartpad.dev/?id=3c299f3972ac18a1b70b841380eaffef
and it seems to be working fine, because without the Navigator the push just replaces the whole page, while with navigator it correctly replaces just the half-side widget.
So is it the way I suppose to do? Any drawbacks - need to use navigatorKey, etc?
P.S. There are of course alternatives like passing InstanceData to each child ctor but that's a lot of manual properties passing around... having the Provider is surely a better way.

Go Router (Flutter)- push the exact same route and refresh that route

With GoRouter, Is there a way to push the exact same route with a different parameter and make the page reload? I am thinking of something like Navigator.pushReplacement
I am using context.go('/my_page/?param={someID}') to push to MyPage (a stateful widget)
The initial push to this route works fine and I load up the page for the particular ID
I am trying to push this same route again (replace the route and reload with different ID) using context.go('/my_page/?param={differentID}'). My breakpoints are hitting the return statement in my GoRoute pageBuilder, and I also hit a breakpoint in MyPage.build. I see the new ID passed into this widget when breakpointing in the build.
But the Page does not rebuild visually (or break in initState - side note, init state is used to load up a couple cubits with the passed in ID - could the page being a stateful widget be the problem?)
Maintain state on the Material Page is false. Also, pushing different routes works just fine.
Is this a stateful widget issue (meaning relocate all of my cubit init calls)? or is there a different way to push the same route?
EDIT _____________
This question was specific to rebuilding the same route, but the greater problem I was working on was to infinitely drill into the same page over and over again, while maintaining the navigation stack.
I was using a state manager to hold a list of routes and trigger the navigation by using context.go() to replace the current route completely.
is there a way to dynamically nest routes with context.push() while maintaining the entire nav stack?
After some research, I have noticed that you can redirect to the same page by using pageKey: UniqueKey() in the definition of the GoRoute().
Example code from my project:
GoRoute(
path: RoutePaths.serviceDetail.value,
name: RouteNames.serviceDetail.value,
pageBuilder: (context, state) {
return CustomTransitionPage<void>(
key: UniqueKey(),
child: const ServiceDetailPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
PageTransitions.subpageTransition(animation, child),
);
},
),
I think context.replace would accomplish what you are trying to do, since it should replace what is currently on the stack.

Flutter logout to remove all routes

Below is my app widget tree. If user is not logged in then login page is shown if user is logged in then work page. on account page I have logout button.Logout button implements push replacement and shows login page.
Issue is - When user click on back button again work page is shown.
How to remove all the routes from widget tree and only show login page after logout action?
Note - I am not using Named routes, just Push , Pop and Replacement
If you are using namedRoutes, you can do this by simply :
Navigator.pushNamedAndRemoveUntil(context, "/login", (Route<dynamic> route) => false);
Where "/login" is the route you want to push on the route stack.
Note That :
This statement removes all the routes in the stack and makes the pushed one the root.
Close your dialog by calling,
Navigator.pop(context);
and then call pushReplacement
void _doOpenPage() {
navigator.pushReplacement(
MaterialPageRoute(builder: (BuildContext context) => MyHomePage()));
}
pushReplacement Replace the current route of the navigator by pushing the given route and then disposing the previous route once the new route has finished animating in.
Read More here

Calling Navigator.of anywhere without context?

I've been setting up a tabbed navigation app based on this wonderful tutorial (https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf).
Now I would like to display a modal overlay login route that covers the whole screen. My login controller checks if the user is logged in and I would like to fire an event on which the modal login route appears. The problem I have is now, that I don't have a context object where I receive the signal to display the login route:
Navigator.of(context).pushReplacementNamed('/');
How can I solve this or is this the wrong approach?
My User controller is a singleton object that gets initiated at app start. It checks then the user data model and if that is not set, it wants to invoke the login screen / route.
Thanks for any pointer in the right direction.
Martin
Define a navigator key that accesses from everywhere in-app (e.g in main class global space ) and pass it to root MaterialApp navigator key property in the build method
final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
then :
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
//...
);
}
then you can access context everywhere with doing like this:
navigatorKey.currentContext
example of navigating:
Navigator.of(navigatorKey.currentContext).pushReplacement(
MaterialPageRoute(builder: (_) => SecondScreen()));
You could use a globalKey to access the context of the widget u like (you have to pass it a key in the constructor) , and leave it in the global space, or static in any class....
although not the most elegant approach, should work
you can use this package to skip the required context.
https://pub.dev/packages/one_context
// go to second page using named route
OneContext().pushNamed('/second');
// go to second page using MaterialPageRoute
OneContext().push(MaterialPageRoute(builder: (_) => SecondPage()));

Logout from non-Widget class

My app makes multiple requests to the server. Sometimes the server may ask the user to re-login, similar to this question:
Flutter: how to force an application restart (in production mode)?
I could do something like this,
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => LoginPage()),
(Route<dynamic> route) => false);
but I need to have a BuildContext for this.
Is there a way to get a current (most recently used) context while in a non-Widget class? I know I can pass the context as an argument every time I'm making a server call, but I hope to find a less intrusive way to do this.
The reason you are looking for context is because you'd like to get hold of the Navigator. (All the Navigator.pushXXX calls do a Navigator.of(context) under the hood to find the (typically) one and only navigator up near the top of the widget tree.)
If what you really want is just access to the navigator, there's another way to do this. Make your app stateful and put a global key in its state.
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
In the app's build (or top inherited widget's build), when you create the material app, pass it the key.
#override
Widget build(BuildContext context) {
return _SomeInherited(
data: this,
child: MaterialApp(
navigatorKey: navigatorKey,
title: 'Some Title',
theme: someTheme,
home: FrontPage(),
),
);
}
Now, from the level of the app, you can use navigatorKey.currentState.pushXXX