How can I implement navigation routes in flutter - flutter

I have design my scenario to understanding about the flow.
Dashboard Screen=> open side Drawer =>Select any menu=> First screen=> second screen=>open dialog(second screen)=> First screen=> Side drawer.
I want to use above like this navigation flow, when I click inside the dialog button I want to go First screen than first screen to click back button so go to side drawer.
I'm using the below code for navigate screen.
Navigator.of(context).pushNamed(screenName);
Please suggest me to how I can implement my above mention flow.

you can give each screen a route name like this :
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
"/home_page" : (context) => MyHomePage(),
"/keep_alive": (context) => const KeepAliveExample()
},
initialRoute: "/home_page",
);
}
}
like this you will set all of your routes , and set an initial route that the application will run at the start.
when you want to navigate to another screen you will do this :
Navigator.of(context).pushNamed(screenName);
and the screen name will be the route that you give it in the material app.
you can change the pushNamed to others styles navigations like :
Navigator.of(context).pushNamedAndRemoveUntil(newRouteName, (route) => false);
Navigator.of(context).pushReplacementNamed(routeName);
Navigator.of(context).popAndPushNamed(routeName);

Related

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

home of materialApp in dart language

my question is about the structure of the widgets.
this line of code:
return new MaterialApp(
title: "question",
home: MyApp(),
);
if there is a navigator on MyApp() class to navigat to another Screen (LoginScreen()), the MyApp() class will be as its parent or will be destried and the other Screen (LoginScreen()) will be as
this line of code:
return new MaterialApp(
title: "question",
home: LoginScreen(),
);
The MaterialApp already provides a Navigator. You should only have one MaterialApp in your app and all your screens should be children of the one app.
MyApp -> MaterialApp
-> HomeScreen
-> LoginScreen
You can follow this basic example on flutter.io: https://flutter.dev/docs/cookbook/navigation/navigation-basics
Also you don't need the new keyword anymore. Any IDE (VSCode/IntelliJ) should give you a hint there if it is correctly configured.

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.

Where to place a Provider Widget -- Flutter Provider Package

I am currently learning app development with Flutter and have started learning about the Provider package. I was having some difficulty and was getting the error:
"Could not find the correct Provider above this ... Widget"
I ended up moving the Provider widget to wrap around my MaterialApp widget instead of my Scaffold Widget, and that seemed to fix things.
That being said, I'm not sure why this fixed things. Are we supposed to put our Provider widget around our MaterialApp? If so, can someone please explain why this is needed? If not, can someone explain how to determine where to place the Provider widget in our tree?
Usually, the best place is where you moved it, in the MaterialApp. This is because since that is where the app starts, the node tree will have access to the provider everywhere.
If your page is a Stateful widget - inside Widget wrap State with Provider, so you can use it inside of State. This is a much cleaner solution because you won't have to wrap your entire application.
If you need the functionality of Provider everywhere in the app - yes, wrapping the entire app is completely fine, though I'll prefer to use some kind of service for this
You could add it to any route and pass it to the route you need to use or you can add it to MaterialApp
so you can use it anywhere.
The best practice of using provider:
Place the Provider widget at the top of the widget tree. Bellow I put a template code that can be used for one more providers at the same place, by using MultiProvider widget under Provider package.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ProviderName<ModelName>(create: (_) => ModelName()),
AnotherProviderName<AnotherModelName>(create: (_) => AnotherModelName()),
],
child: MaterialApp(
title: 'App title',
theme: ThemeData(
primarySwatch: Colors.blue,
primaryColor: const Color(0xFF2196f3),
accentColor: const Color(0xFF2196f3),
canvasColor: const Color(0xFFfafafa),
),
home: MyHomePage(), // Your widget starting
),
);
}
}
For more informatin: https://pub.dev/documentation/provider/latest/

Something like conditional routing in Flutter?

Consider I have 3 screens namely screen 1, screen 2, screen 3 and screen 4.
I want to achieve the following.
Screen 3 is opened by Screen 1 -> BackButton -> Screen 2
Screen 3 is opened by Screen 2 -> BackButton -> Screen 3
Screen 3 is opened by Screen 4 -> BackButton -> Screen 1
Moreover, iOS automatically sets a swipe back option. I want to overwrite it that a swipe back in iOS does the same as described above.
Is there something like conditional routing in Flutter which helps me to adjust the BackButton-behaviour in accordance to 'from which Screen was my current Screen opened (navigator.push)'?
Wrap your widget tree in a WillPopScope() widget. This widget has an onWillPop property that you can override to whatever you want - in this case, depending on the screen you're on you'll probably want to override it to
onWillPop: () => Navigator.pushReplacement(<correctScreenWidget>)
This should catch any attempts to go back and instead do whatever you override it to. Be sparing with it, overriding default back button behaviour can make for a weird user experience if done poorly.
As for the conditional part of it, unfortunately it's a bit tricky as Navigator._history is private, so we can't just check the previous route that way. Best bet is to set up a NavigatorObserver to keep track of previous routes, and set the name in the RouteSettings of each of your routes to keep track.
Step one is to create an observer and provide it to your Navigator, something like this:
class PreviousRouteObserver extends NavigatorObserver {
Route _previousRoute;
Route get previousRoute => _previousRoute;
String get previousRouteName => _previousRoute.settings.name;
#override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
_previousRoute = previousRoute;
}
#override
void didReplace({Route<dynamic> newRoute, Route<dynamic> oldRoute}) {
_previousRoute = oldRoute;
}
}
class MyApp extends StatelessWidget {
final PreviousRouteObserver observer = PreviousRouteObserver();
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Flutter Demo Home Page', observer: observer),
navigatorObservers: [
observer,
],
);
}
}
Note that MyHomePage above needs to accept the observer as an argument so you can access it. Alternatively you could set up an InheritedWidget or something to maintain access to it, but this answer is getting a little long already so I'll leave that for a later question.
Then, when providing Routes to your Navigator, ensure you've got a name in the RouteSettings:
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => NextScreen(observer: widget.observer),
settings: RouteSettings(name: "nextScreen"),
),
);
Finally, do conditional routing based on the current value of widget.observer.previousRouteName in any widget that has access to it. This is just a simple matter of a switch or something in your onWillPop, so I'll leave that to you.
Kind of unfortunate that it's so roundabout, but it looks like this might be your best option at the moment. Hope it helps!