Flutter web redirect user if not authentiacted - flutter

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

Related

Flutter image assets not loading after redirect with go_router & login

I have a small Flutter app with 2 screens (/login & /home) and I use go_router to navigate and animated_login. The assets are placed on the home screen and they load fine if I directly access the screen, so pubspec.yaml is correctly defined.
The images fail to load only when I redirect to /home after /login. One interesting observation is that when this happens, the Flutter dev server seems to be hanging (stops responding, but doesn't crash, can't restart it with R, the browser tab complains that it lost connection to the server etc.). This problem occurs also with a combination of auto_route and flutter_login.
Thanks for any hints.
Router setup (tried also w/ the redirect parameter at router level rather than individual routes):
GoRouter routerGenerator(UserData userData) {
return GoRouter(
initialLocation: Routes.home,
refreshListenable: userData,
debugLogDiagnostics: true,
routes: [
GoRoute(
path: Routes.home,
builder: (_, __) => BasicScreen(),
redirect: (state) => userData.loggedIn ? null : Routes.login
),
GoRoute(
path: Routes.login,
builder: (_, __) => AnimLoginScreen(),
redirect: (state) => !userData.loggedIn ? null : Routes.home
),
GoRoute(path: '/', builder: (_, __) => BasicScreen())
]);
}
abstract class Routes {
static const home = '/home';
static const login = '/login';
}
Main app:
void main() {
runApp(
MultiProvider(providers: [
//other providers here
ChangeNotifierProvider(create: (_) => UserData()),
], child: MyApp()),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
final router =
routerGenerator(Provider.of<UserData>(context, listen: false));
return MaterialApp.router(
title: 'Playground',
routeInformationParser: router.routeInformationParser,
routeInformationProvider: router.routeInformationProvider,
routerDelegate: router.routerDelegate,
);
}
}
Basic screen:
class BasicScreen extends StatelessWidget {
BasicScreen({super.key});
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Image(image: AssetImage("assets/images/image1.png")),
Image(image: AssetImage("assets/images/image2.png")),
Image(image: AssetImage("assets/images/image3.png")),
],
),
);
}
}
Solution
Provide a simple proxy over both Flutter DevTool & backend services with SSL capabilities.
Explanation
The issue has nothing to do with routing, but rather the dev setup. Our backend services require SSL connections, but Flutter dev tool doesn't support that. Flow:
Flutter DevTool starts the project (plain HTTP) and opens Chrome window.
Assets load ok.
User logs in, backend service requires HTTPS for secure cookies.
Browser upgrades all localhost connections to HTTPS.
Flutter DevTools fails to provide SSL connection.
Assets fail to load.
The hanging DevTool is caused by the same issue: seems to me that the DevTool is pending the WebSocket connection to be opened by the JS code running in the browser, but as the browser initiates an HTTPS connection to the DevTool, it even fails to load the JS code. Therefore, the DevTool never completes the init process (as it has no incoming connection).

GoRouter - Web - How to detect that the user entered an URL in the browser's address bar?

I'm using go_router on web and I would like to implement a different redirect logic between the call of the go_router's API (push, go, replace) and when the user enters an URL in the browser address bar.
I didn't find any solution and I was going to implement it this way:
If I understood correctly, when the user enters an URL in the browser address bar, it restarts the application from scratch, while calling the API doesn't. And I came to this:
bool _isFirstRedirect = true;
GoRouter(
routes: myRoutes,
redirect: (state) {
if (_isFirstRedirect) {
// Logic when the user enters an URL in the browser's address bar.
_isFirstRedirect = false;
} else {
// Logic when it comes from a call of the go_router's API.
}
},
);
But I'm not sure that is a good design. Is there a better way to do that?
Use initialLocation in GoRouter constructor. Something like this:
GoRouter(
initialLocation: '/page3',
routes: [
GoRoute(
path: '/',
builder: (context, state) => const Page1Screen(),
),
GoRoute(
path: '/page2',
builder: (context, state) => const Page2Screen(),
),
GoRoute(
path: '/page3',
builder: (context, state) => const Page3Screen(),
),
],
);

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.

onGenerateRoute not redirecting with unauthenticated user

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

How to get argument from url in flutter web using get: ^3.26.0

return GetMaterialApp(
title: title,
theme: appTheme,
getPages: [
GetPage(name: '/', page: () => Splash()),
GetPage(name: '/update', page: () => Update()),
GetPage(name: '/login', page: () => SignIn()),
GetPage(name: '/reference', page: () => Reference()),
],
home: Splash(),
);
this is my route code and now i want to user enter url like : xyz.com/login/jayesh , then i want to get user name in login screen using getx state management.
In your route:
xyz.com/login?username=jayesh
And in your controller or in your view:
var username = Get.parameters["username"];
A good place to get your arguments or parameters may be in your controllers onInit or your views build method
add page with name in GetMaterialApp
getPages:[
GetPage(name: '/login/:refId', page: () => SignIn()),
],
you can retrive data(perameter) like:
var data = Get.parameters;
String id = data['refId'];