Giter Site home page Giter Site logo

Comments (13)

Milad-Akarie avatar Milad-Akarie commented on May 22, 2024 2

Hey @frank06, yes. You're not supposed to use the canNavigate func manually,
Just let your guard do the redirecting for you like follows

class AuthGuard extends RouteGuard {
  @override
  Future<bool> canNavigate(
      BuildContext context, String routeName, Object arguments) async {
    if (!isAuthenticated) {
        Router.navigator.pushNamed(Router.loginScreen)
        return false;
    }
    return true;
  }
}

You could have the guard await for the LoginRoute results and return true if user logs in successfully so you still navigate to your secure route after

Future<bool> canNavigate(
      BuildContext context, String routeName, Object arguments) async {
    if (!isAuthenticated) {
       var authenticated await Router.navigator.pushNamed(Router.loginScreen);
        // if authenticated is true you still navigate to your secure route
        return authenticated;
    }
    return true;
  }

Next annotate your secure routes with GuardedBy([AuthGuard]) and pass in your guards as types.

class $Router {
  @initial
  HomeScreen homeScreen;

  @GuardedBy([AuthGuard])
  SecureScreen secureScreen;

  @GuardedBy([AuthGuard])
  SecondScreen2 secondScreen2;

  LoginScreen loginScreen;
}

now any route annotated with @GuardedBy will go through the assigned guards before it can navigate to its destination.
Hope this helps.

from auto_route_library.

frank06 avatar frank06 commented on May 22, 2024

Great, thanks @Milad-Akarie !

I am not able to make this work with my screen design (slightly different than yours):

In my app there are NO public screens (i.e. all screens, except LoginScreen should be guarded by AuthGuard). My users have to be logged in to see the home page.

My issue is that canNavigate is never called with the following setup.

Why?

import 'package:auto_route/auto_route.dart';
import 'package:auto_route/auto_route_annotations.dart';

import 'main.dart';

@MaterialAutoRouter()
class $Router {
  @initial
  @GuardedBy([AuthGuard])
  HomeScreen homeScreen;

  // @CustomRoute(returnType: bool)
  LoginScreen loginScreen;
}

class AuthGuard extends RouteGuard {
  @override
  Future<bool> canNavigate(context, routeName, arguments) async {
    throw UnsupportedError('never reaches this');
    return true;
  }
}
import 'package:auto/router.dart';
import 'package:flutter/material.dart';

import 'router.gr.dart';

void main() {
  Router.navigator.addGuards([AuthGuard()]);
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Auto example',
        theme: ThemeData(primarySwatch: Colors.amber),
        onGenerateRoute: Router.onGenerateRoute,
        navigatorKey: Router.navigator.key,
        initialRoute: Router.homeScreen,
      );
  }
}

class LoginScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(child: Text('login page'));
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(child: Text('secure area'));
  }
}

from auto_route_library.

Milad-Akarie avatar Milad-Akarie commented on May 22, 2024

Unfortunately, the navigator widget within the MaterialApp is the one pushing the initial route, so there's no way to intercept and guard it with the current approach.

You could always have a public initial route that shows a loading indicator or something until the real distention is decided.

then in the initial route push and replace the current route with your secureScreen

 void initState() {
    Router.navigator.pushReplacementNamed(Router.secureScreen);
    super.initState();
  }

from auto_route_library.

frank06 avatar frank06 commented on May 22, 2024

Thanks!

I ended up calling this stateful widget RedirectRoute, and using it like so:

MaterialApp(
        title: 'Auto example',
        theme: ThemeData(primarySwatch: Colors.amber),
        onGenerateRoute: Router.onGenerateRoute,
        navigatorKey: Router.navigator.key,
        home: RedirectRoute(Router.homeScreen),
      ),
@MaterialAutoRouter()
class $Router {
  @GuardedBy([AuthGuard])
  HomeScreen homeScreen;

  @CustomRoute(returnType: bool)
  LoginScreen loginScreen;
}

class AuthGuard extends RouteGuard {
  @override
  Future<bool> canNavigate(context, routeName, arguments) async {
    var auth = Provider.of<AuthState>(context);
    if (!auth.value) {
      auth.value = await Router.navigator.pushNamed(Router.loginScreen);
    }
    return auth.value;
  }
}

It does work fine (successfully triggers canNavigate), with a caveat.

There can't be a route decorated with @initial, as it seems the home route must be treated like / somewhere and that ends up in an infinite loop.

Also, since the login page communicates success/failure via pop<bool>, the login page always shows a back button in Material. When opening the app the first time and seeing the login screen, clicking on the back button throws an exception.

_AssertionError (Failed assertion: boolean expression must not be null) (https://github.com/Milad-Akarie/auto_route_library/blob/master/auto_route/lib/src/extended_navigator.dart#L101)

That said I'd LOVE an API like:

class $Router {
  @initial
  @GuardedBy([AuthGuard])
  HomeScreen homeScreen;

  @CustomRoute(returnType: bool)
  LoginScreen loginScreen;
}

with an initialization like:

MaterialApp(
        title: 'Auto example',
        theme: ThemeData(primarySwatch: Colors.amber),
        onGenerateRoute: Router.onGenerateRoute,
        navigatorKey: Router.navigator.key,
        home: Router.initialRoute,
      ),

(initialRoute redirect widget provided by the library?)

And no back button issues with the login screen.

Loving the library so far. Thanks.

from auto_route_library.

Milad-Akarie avatar Milad-Akarie commented on May 22, 2024

Hey @frank06, I don't think it's a good idea to work with widgets and named routes at the same time, why can't the initial route (The Redirect Widget) be named?
Navigator will automatically push the initial route you pass to material App which defaults to "/" if you don't pass anything. then inside of the initial route push your guarded route as a REPLACEMENT so the initial route is poped first.

from auto_route_library.

frank06 avatar frank06 commented on May 22, 2024

why can't the initial route (The Redirect Widget) be named?

Because a widget accepts a parameter (the redirect "to" route name). Afaik this is not possible with a named route? So I would have to make a specific stateful widget just for redirecting (with a hardcoded route), and add it to the routing configuration. My intention was to make this easier and more generic.

My app doesn't show the Material back button anymore – so not a problem for me. But I think there's still an issue there.

from auto_route_library.

frank06 avatar frank06 commented on May 22, 2024

Thank you for your help @Milad-Akarie

from auto_route_library.

Milad-Akarie avatar Milad-Akarie commented on May 22, 2024

Hey, @frank06 what did you end up doing?

from auto_route_library.

frank06 avatar frank06 commented on May 22, 2024

I ended up doing this, works great so far!

        onGenerateRoute: Router.onGenerateRoute,
        navigatorKey: Router.navigator.key,
        home: RedirectTo(Router.dashboardScreen),

from auto_route_library.

Milad-Akarie avatar Milad-Akarie commented on May 22, 2024

How does the RedirectTo Widget look like?

from auto_route_library.

frank06 avatar frank06 commented on May 22, 2024
class RedirectTo extends StatefulWidget {
  final String redirectTo;
  const RedirectTo(this.redirectTo, {Key key}) : super(key: key);
  @override
  _RedirectToState createState() => _RedirectToState();
}

class _RedirectToState extends State<RedirectTo> {
  @override
  void initState() {
    Router.navigator.pushReplacementNamed(widget.redirectTo);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Loading ${widget.redirectTo}...'),
      ),
    );
  }
}

from auto_route_library.

Milad-Akarie avatar Milad-Akarie commented on May 22, 2024

I believe that the navigator will still automatically navigate to '/' even if you specify a home widget. This will cause your dashboard to be called twice! right?

from auto_route_library.

frank06 avatar frank06 commented on May 22, 2024

I just put a breakpoint on DashboardScreen#build and it stops only once.

As far as I understand, MaterialApp will use either home OR initialRoute

from auto_route_library.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.