How to restore last route in Flutter - flutter

I'm developing a Flutter app and I have a problem.
In my main.dart I set a home route that is "FirstScreen", then the user can go to the LoginScreen to sign into the account. So if I press the home button and then I try to re-open the app the screen that appear is FirstScreen, so how can I show the last route the user have seen?
I've searched on Flutter docs and on other question in StackOverflow for some solutions but I found nothing that work for me.
This is my main.dart build.
Widget build(BuildContext context) {
return MaterialApp(
home: FirstScreen(),
routes: {
'/screen1' : (context) => Screen1(),
'/homeScreen' : (context) => HomeScreen(navigatorKey: navigatorKey,),
'/registerScreen' : (context) => RegisterScreen(),
'/screen2' : (context) => Screen2(),
'/firstScreen' : (context) => FirstScreen(),
'/userProfileScreen' : (context) => UserProfileScreen(),
},
navigatorKey: navigatorKey,
);

You'll need to keep track of the latest route the user has visited and store that somewhere persistent so that it survives app restarts. Perhaps look at the shared preferences package as a simple approach:
https://pub.dev/packages/shared_preferences
When the app starts, look in shared preferences to see if you have the previous route stored, and navigate to it (or set it as the home route). If not, fall back to the FirstScreen as a default.

I've find a solution, using WidgetBindingObserver, and put this line : WidgetsBinding.instance.addObserver(this); inside every StatefulWidget initState.

Related

ModalRoute.withName never returns true in pushNamedAndRemoveUntil

I have been wrestling with this issue for a couple of days now and I haven't been able to find a solution, hence the post.
At one point in my app, I need to pop 2 routes from the navigator stack and push a new route. After researching the best way to do this, I have found that using pushNamedAndRemoveUntil is the best way, as I can specify ModalRoute.withName('/<route_name>') and it will pop the routes until it reaches /<route_name> at which point it will stop and push the new route. This is the line I have been using Navigator.of(context).pushNamedAndRemoveUntil('/raceadmin_reporting', ModalRoute.withName('/raceadmin_page'));.
My issue though is that it doesn't work for me. It doesn't seem to matter what I put in /<route_name>, pushNamedAndRemoveUntil pops all the routes, which leads me to believe that ModalRoute.withName never returns true.
I have also tried Navigator.of(context).pushNamedAndRemoveUntil('/raceadmin_reporting', (route) => route == RaceAdminPage.route()); and it doesn't work either.
When I look at the debugger, this is what I see:
App Navigator Stack
The route I am trying to pop until is the RaceAdmin page, which is clearly in the stack. In the definition of that class, I added the line static const routeName = '/raceadmin_page'; which is what I call in ModalRoute.withName('/raceadmin_name') and it doesn't work.
My routes are defined in the routes.dart file as per below:
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (context) => RailMeatApp());
case '/raceadmin_page':
return MaterialPageRoute(builder: (context) => const RaceAdminPage());
case '/raceadmin_pendinglist':
return MaterialPageRoute(builder: (context) => PendingResultsList());
case '/raceadmin_pendingresults':
final args = settings.arguments as Map<String?, String?>;
return MaterialPageRoute(
builder: (context) => PendingResultsPage(
raceId: args['raceId'] as String,
));
case '/raceadmin_reporting':
return MaterialPageRoute(builder: (context) => RaceAdminReporting());
default:
return _errorRoute();
}
}
And my MaterialApp is defined as per below:
return MaterialApp(
navigatorKey: _navigatorKey,
home: _railmeatHome(),
onGenerateRoute: RailmeatRoutes.generateRoute,
);
}
In the Flutter debugger, I can look at the MaterialApp widget and I can see in its state that the navigator has 4 entries in its _history property as shown below:
MaterialApp Widget Properties
If I click on any of the entries in the _history, I see the the same info as below:
Route property in _history
As you can see, the name property under _settings is null, which, in my mind, would explain why ModalRoute.withName can't find the right route, but I am not sure that my thinking is accurate.
What should I do differently to make pushNamedAndRemoveUntil work?
Thanks a lot in advance,
Bertrand.
Try this instead
Navigator.of(context)
.pushNamedAndRemoveUntil('/routename', (Route<dynamic> route) => false);
Or
Navigator.of(context).popUntil(ModalRoute.withName('/root'));

Flutter - Error in hot reload using lazy internationalization

I'm building an application that uses lazy internationalization, this way there will be no translation files in the application and all translations will be fetched from the internet when a new page is opened. For that I am using a localization cubit.
Each screen of my application is divided into a "view" that receives the translated messages as a parameter, a "cubit" that contains the cubit screen and its states, and a "container" that contains the BlocProvider for the cubit and the screen.
For now my app starts in the presentation screen, after that it goes to the login screen and finally goes to the home screen.
So in the main file, instead of using the presentation screen directly, I use the localization container and the presentation container comes as its child:
return MaterialApp(
title: 'My App',
theme: myTheme(context),
debugShowCheckedModeBanner: false,
home: LocalizationContainer(
child: PresentationContainer(),
),
);
The PresentationContainer is composed this way:
class PresentationContainer extends BlocContainer {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => PresentationCubit(),
child: I18NLoadingContainer(
language: BlocProvider.of<CurrentLocaleCubit>(context).state,
viewKey : "Presentation",
creator: (messages) => PresentationView(PresentationViewLazyI18N(messages)),
),
);
}
}
So in the container I have a BlocProvider with PresentationCubit and I18NLoadingContainer as a child.
I18NLoadingContainer just obtains the transalted messages according to the language provided and the screen name, that is "Presentation" in this case. The translated messages are returned in the variable messages, so this messages are passed as parameter to the screen.
If I use this only for my presentation screen everything works fine, but the issue comes when I need to open a new page.
After the presentation screen I need to open the login screen. So in the PresentationView I have the following function when the user clicks the button to open the login screen:
void _goToLogin(BuildContext blocContext) {
Navigator.of(blocContext).pushReplacement(
MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: BlocProvider.of<CurrentLocaleCubit>(blocContext),
child: LoginContainer(),
),
),
);
}
And the LoginContainer works exaclty as the PresentationContainer:
class LoginContainer extends BlocContainer {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => LoginCubit(),
child: I18NLoadingContainer(
language: BlocProvider.of<CurrentLocaleCubit>(context).state,
viewKey : "Login",
creator: (messages) => LoginView(LoginViewLazyI18N(messages)),
),
);
}
}
If I keep in the presentation screen and use the hot reload everything works fine, but if I open a new screen using this method, I got the following error when try to use hot reload:
The following _CastError was thrown building Builder(dirty): Null
check operator used on a null value
I'm not sure your LoginContainer is still wrapped by the LocalizationContainer when you change the route. I would suggest you to provide a CurrentLocaleCubit above the MaterialApp widget and check whether it's working or not. I think you're loosing a CurrentLocaleCubit instance

Flutter Navigator.pushReplacementNamed show back button at the page

I'm using Navigator.pushReplacementNamed in flutter app to navigate from loginpage to homepage working well, but in my homepage show back arrow button at the appBar and it returns to loginpage when pressing it. What should I do? I tried put leading: Text(''), but when pressing physical back button it still goes back to loginpage.
I have a logout button and I want user to logout only by this button, not from back button
*this is my first question here, sorry for my poor English or any mistakes
You should use this instead:
.pushNamedAndRemoveUntil(/* Your Route */, (Route<dynamic> route) => false)
As I understand your UX flow, the user will always be directed to the login page on the app start. If the user is already logged in, you should avoid navigating to this (at that moment useless) route. Instead try to distinguish in the build method if the user is logged in or not. If the user is already logged in, build the homepage. If the user is not logged in, build the login page. As soon as the user logs in, the state changes and the homepage will be build.
// your stateful component
bool isLoggedIn;
#override
Widget build(BuildContext context) {
if(isLoggedIn) {
return _buildHomepage();
} else {
return _buildLoginPage();
}
}
Widget _buildHomepage() {
// build your homepage
}
Widget _buildLoginPage() {
// build your login page
}
I had this problem today and came across your Post.
While PushNamedAndRemoveUntil works, the solution in my case was even more simple:
Make sure you're not Naming a route, which is not your Homescreen '/'.
Something like:
MaterialApp(
title: 'Flutter Demo',
initialRoute: Screen1.routeName, // routeName = "/route1"
routes: {
Screen1.routeName: (ctx) => Screen1(), // routeName = "/route1"
Screen2.routeName: (ctx) => Screen2(), // routeName = "/"
Screen3.routeName: (ctx) => Screen3(), // routeName = "/screen2"
},
);
})
Will start your App with Screen1, but place Screen2 at the top of the Navigator's Stack.

How to remove backstack including home page in flutter

I have seen many examples but none of them providing me a way to remove the entire back stack(including the home page) while navigating to the next page.
Eg: I have a few login pages once a user successfully entered login credentials user move to the home screen, so here I want to remove all previous screen which appeared till now, How can I do that?
Currently using code:
Navigator.of(context).pushNamedAndRemoveUntil(
HomeScreen.route_name, ModalRoute.withName('/'));
To remove all the routes below the pushed route, use a RoutePredicate that always returns false (e.g. (Route route) => false).
So for your code to work as expected, remove ModalRoute.withName('/') and give a route predicate which returns false. So it should be
Navigator.of(context).pushNamedAndRemoveUntil(
HomeScreen.route_name, (Route<dynamic> route)=>false);
For reference see the official documentation
Try this out. This code push the given route onto the navigator and then remove all the previous routes even HomeScreen.
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => HomeScreen(),
),
(route) => false,
);
Note: This is tested on my project. So I hope it will 100% work
Try this way
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (dialogContex) => HomePageScreen()),
ModalRoute.withName("/HomePageScreen"));
or with Getx:
Get.offNamedUntil('home', (route) => false);

How to scope a ChangeNotifier to some routes using Provider?

I have a ChangeNotifier, and I would like to share it between multiple routes but not all routes:
Page1 is my first page. I need share data of ChangeNotifierProvider with Page2, Page3 and Page only and on enter Page1 call dispose of my ChangeNotifierProvider.
How can I do this using provider?
To do so, the easiest solution is to have one provider per route, such that instead of:
Provider(
builder: (_) => SomeValue(),
child: MaterialApp(),
)
you have:
final value = SomeValue();
MaterialApp(
routes: {
'/foo': (_) => Provider.value(value: value, child: Foo()),
'/bar': (_) => Provider.value(value: value, child: Bar()),
'/cannot-access-provider': (_) => CannotAccessProvider(),
}
)
It is, on the other hand, not possible to have your model "automatically disposed".
provider is not able in such a situation to know that it is safe to dispose of the object.