onGenerateRoute not redirecting with unauthenticated user - flutter

I am trying to redirect users to my login page if they visit my site without being logged in. I'm trying to use onGenerateRoute to do this. My code in main is this:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseAuth auth = FirebaseAuth.instance;
bool isAuth = auth.currentUser!=null ? true : false;
runApp(MaterialApp(
initialRoute: '/initial',
title: 'Repeater Delivery',
theme: ThemeData(...),
onGenerateRoute: (settings) {
if (!isAuth){
print(settings);
return MaterialPageRoute(
builder: (context) => Login(),
settings: RouteSettings(name: '/login'),
);
}
},
routes: {
'/initial': (context) => Home(),
'/home': (context) => Home(),
'/login': (context) => Login(),
'/signup': (context) => Signup(),
'/how_it_works': (context) => HowItWorks(),
'/profile': (context) => UserProfile(),
},
));
}
The page enters the if statement, so I know that that's working correctly, but I'm not sure how to then redirect to my login page. Also, when I go to a page on my site like www.site.com/#/profile and print out the settings from onGenerateRoute, it just prints a setting name of '/' instead of '/profile'. What am I doing wrong?

According to documentation MaterialApp.onGenerateRoute :
The route generator callback used when the app is navigated to a named
route.
If this returns null when building the routes to handle the specified
initialRoute, then all the routes are discarded and
Navigator.defaultRouteName is used instead (/). See initialRoute.
During normal app operation, the onGenerateRoute callback will only be
applied to route names pushed by the application, and so should never
return null.
This is used if routes does not contain the requested route.
The Navigator is only built if routes are provided (either via home,
routes, onGenerateRoute, or onUnknownRoute); if they are not, builder
must not be null.
Here, your initialRoute is passed to onGenerateRoute, which is not always returning a route (because of the Auth condition). So Navigator is giving another route (the "/") as the initial one. onGenerateRoute must never return null on the App launch task.
Your routes property is only used when you're using pushNamed Navigator's method

Related

How can you change URL in the web browser without named routes

Since "named routes are no longer recommended for most applications" how can you change the URL in web browser when you push a new route onto Navigator stack?
E.g. URL is http://localhost:37291/#/ and after performing
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SecondRoute()),
);
I would want it to change to http://localhost:37291/#/secondRoute.
In the same official docs you have provided there is link for the go_router, which is preferred by flutter team
You can use the path property of GoRoute to achieve the desired url
Initial setup:
// GoRouter configuration
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
),
GoRoute(
path: '/secondRoute', // 👈 here you can add the path
builder: (context, state) => SecondScreen(), // This is the desired screen you would want to go to
),
],
);
Now your url when routing to SecondScreen would be http://localhost:37291/#/secondRoute
If you want to have the very same functionality you had with Navigator stack you need to implement stuff around Router class. Find out more in this article. You could possibly refer this article for the same.
Further reference:
Flutter: go_router how to pass multiple parameters to other screen?

How to redirect current page to another using Flutter's go_router?

How can I achieve the same effect as the Navigator.pushReplacement does, but in declarative routing with go_router?
Explained
I have 3 pages: /loading, /login, /. And logic is (pseudocode):
/loading {
performSomeComputation();
if checkAuthorization() == authorized {
go /;
} else {
go /login;
}
}
/login {
waitForUserToLogIn();
go /;
}
/ {
// account logic
}
With go_router you can either use .go() or .push().
.push() pushes a new page onto the stack.
.go() works with sub-routes. Every top-level route has got its own stack. Here is a short example:
final GoRouter _router = GoRouter(
routes: <GoRoute>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) =>
const Page1Screen(),
routes: [
GoRoute(
path: 'page2',
builder: (BuildContext context, GoRouterState state) =>
const Page2Screen(), ),
],
),
GoRoute(
path: '/page3',
builder: (BuildContext context, GoRouterState state) =>
const Page3Screen(),
),
],
);
If you go from the top-level route '/' to the subroute '/page2', '/page2' will be added to the '/' stack. You'll get the back button in the app bar because both '/' and '/page2' are on the stack of top-level route '/'.
If you go from the top-level route '/' to '/page3', you replace the current '/' stack with the '/page3' stack because '/page3' is also a top-level route. You will not see a back button because '/page3' is the first page on the stack of top-level route '/page3'.
What you might be looking for though is redirect. You can pass a redirect function to the GoRouter constructor if you want to redirect on state changes like login and logout. You can combine this with state management libraries like Bloc. You can pass a refreshListenable to the GoRouter constructor that triggers a refresh of the route every time the state stream (yourBloc.stream) changes.
go_router also got a great documentation: https://gorouter.dev/

Flutter GetX - how to implement a loading screen that check the token

So this is the scenario:
I build an app that the first route or the first thing to do is check with a API REST a verification of a token that I already have on localsorage. If the token is valid, proceed to the home page route, if don't we need to redirect them to login screen. All this is async. I already try to get this working adding a route like this:
Widget build(BuildContext context) {
return GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(
name: '/',
page: () => const AppPage(),
middlewares: [AppPageMiddleware()],
),
GetPage(name: '/login', page: () => const LoginPage()),
GetPage(
name: '/operator', page: () => const OperatorHomePage(), transition: Transition.zoom),
],
);
As you can see, I load a AppPage that the only thing that they do is show the loading spinner. The middleware called AppPageMiddleware uses this:
GetPageBuilder onPageBuildStart(GetPageBuilder? page) async {
// here I perform the call to the API and wait for the response.
// If the API response is true, I need to save the response
// and redirect to OperatorHomePage
print('Bindings of ${page.toString()} are ready');
return page!;
}
But I have an error here because I can't add async.
Maybe this is not the best way to do this, if you can suggest me another way, I will appreciate so much. Sorry, I'm new with GetX.

How can i do push replacement using go router plugin in flutter [go router]

I am using the go router plugin for my flutter web application, but I didn't find any way to do push replacement in navigation.
not only push replacement, can we do any other type of navigation link
pushNamedAndRemoveUntil
popUntil
because these navigation options must be needed in kind of any system!
What I tried
context.go(userStorage.redirectURL!);
GoRouter.of(context).go(PagesCollection.adminDashboard);
they only pushed the next page, not replacing
Note: I want this functionality in go router
Try this
Router.neglect(context, () {
context
.goNamed('third', params: {"id": ID});
});
It will neglect your current page. Hop this will helps you
I have had the same problem before. Then this way worked for me
First you need to avoid nested routes:
final GoRouter router = GoRouter(
routes: <GoRoute>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) =>
const OnboardingScreen(),
),
GoRoute(
path: '/login',
builder: (BuildContext context, GoRouterState state) => LoginScreen(),
),
GoRoute(
path: '/signup',
builder: (BuildContext context, GoRouterState state) =>
const SignupScreen(),
),
],
);
Use the above code instead of nesting like this:
final GoRouter router = GoRouter(
routes: <GoRoute>[
GoRoute(
routes: <GoRoute>[
GoRoute(
path: 'login',
builder: (BuildContext context, GoRouterState state) => LoginScreen(),
),
GoRoute(
path: '/signup',
builder: (BuildContext context, GoRouterState state) =>
const SignupScreen(),
),
],
path: '/',
builder: (BuildContext context, GoRouterState state) =>
const OnboardingScreen(),
),
],
);
This will make you able to replace OnboardingScreen() from the above eg. So next you can use pushreplacement future with:
context.go("/routname");
or
GoRouter.of(context).go("/routname");
If u don't want replacement, then can just use:
context.push("/routname");
or
GoRouter.of(context).push("/routname");
You can do it in a way ->
context.pop();
context.push(routeName);
Adding both these commands works similar to pushReplacement.
In the current version of go_router (5.1.10), you can use GoRouter.of(context).replace('/your-route) to do the same as Navigator.of(context).pushReplacement(yourPage).
There is a PR open to add popUntil but it looks like the flutter team doesn't want to support imperative routing methods and only focus on declarative routing (see, this issue).
It means the popUntil PR might never be merged, and pushNamedAndRemoveUntil never be supported.
It also means that push and replace might be removed in the future.
I am using go_router 6 and it works with:
context.pushReplacement()
What you can do is use Sub-Route Redirects in Go Router.
Let's consider you want users to first go on your Homepage and then from there they tap Login, and go to Login page and then after Logging in, they go to Dashboard Page.
But when they press Browser's Back Button they should go to Homepage and not the LoginPage i.e skipping the LoginPage just like "pushNameReplacement".
Then for this to happen you can configure redirects of each LoginPage and Dashboard page and get this functionality.
You can configure it such that whenever user (including from Browser's History) goes to Dashboard link it first checks if its Logged In then it opens otherwise it displays Login Page automatically.
Edited
i use this functions and they are useful for me
void popUntil(String name){
GoRouter router= AppRoutes.router;
String currentRoute=router.location;
while(router.canPop()&&name!=currentRoute){
currentRoute=router.location;
if(name!=currentRoute){
router.pop();
}
}
}
void pushNamedAndRemoveUntil(String name){
GoRouter router= AppRoutes.router;
while(router.canPop()){
router.pop();
}
router.replaceNamed(name);
}
work same as pushNamedAndRemoveUntil(route_name, (Route<dynamic> route) => false);
any suggestion please drop a comment

Flutter web redirect user if not authentiacted

I have a GetMaterialApp with named routes to be used in Flutter web for a dashboard project. However, I am struggling to check whether the user is authenticated and redirect the user depending on the auth state. This is my App Code:
GetMaterialApp(
initialBinding: InitialBinding(),
debugShowCheckedModeBanner: false,
theme: CustomTheme().getLightTheme,
themeMode: ThemeMode.light,
title: "----------",
initialRoute: "/login",
unknownRoute: GetPage(name: "/notfound", page: () => NotFoundPage()),
getPages: [
GetPage(name: "/login", page: () => Root()),
GetPage(name: "/dashboard", page: () => Dashboard()),
],
),
I am using GetX to handle the authentication state. Therefore, I have access to the authentication state throughout the web app. The Root widget consists of a simple Obx to check the authentication state and send the user to the dashboard:
return Obx(() => (Get.find<AuthController>().loggedIn) ? Dashboard() : LoginScreen());
Sadly, that does not do the trick, as the url does not change, simply the content does. The url remains at /login.
I could simply call Get.toNamed("dashboard") when the user is logged in, but then the dashboard page would be exposed to the url, allowing the user to reach the /dashboard url even if he is not logged in.
I also do not want to create a check in every Widget or page I create, since that would be inefficient. Is there a way to check whether the user is logged in and, if not, redirect the user to the login page on every url entered?
Example:
www.adress.com/dashboard would redirect to www.adress.com/login if the user is not logged in
Is there a way to generally check the auth-state and redirect the user accordingly using GetX?
Side Note: I am able to get the correct authentication state every time, that is not the problem, as I use GetX.
Short Summary:
Is it possible to check the auth state outside the actual widget (e.g. in the GetMaterialApp) and redirect any not-authenticated users to the login page, even if they type in the /dashboard url?
Use middleware for check if user is authenticated or not
GetMaterialApp(
initialBinding: InitialBinding(),
debugShowCheckedModeBanner: false,
theme: CustomTheme().getLightTheme,
themeMode: ThemeMode.light,
title: "----------",
initialRoute: "/dashboard",
unknownRoute: GetPage(name: "/notfound", page: () => NotFoundPage()),
getPages: [
GetPage(name: "/login", page: () => Root()),
GetPage(name: "/dashboard", page: () => Dashboard(), middleware: [AuthMiddleware()]),
],
),
class AuthMiddlware extends Middlware {
RouteSetting? redirect(String? route) => !isAuthenticated ? RouteSetting(name="/login") : null;
}
If the user requests an arbitrary page within your web app, then you may want to intercept with the login page before continuing to the requested page. You can do this by passing a return url to the login page...
main.dart
GetMaterialApp(
...
initialRoute: "/dashboard",
getPages: [
GetPage(name: "/login", page: () => Root()),
GetPage(
name: "/dashboard",
page: () => Dashboard(),
middlewares: [AuthMiddleware()]
),
],
),
class AuthMiddleware extends GetMiddleware {
RouteSettings? redirect(String? route) {
String returnUrl = Uri.encodeFull(route ?? '');
return !isAuthenticated
? RouteSettings(name: "/login?return=" + returnUrl)
: null;
}
}
Then in your login page, after logging in you can direct back to where the original request intended...
login.dart
//after successful login...
String returnUrl = Get.parameters['return'] ?? '/';
Get.offAllNamed(returnUrl);