SakoDaemon
SakoDaemon

Reputation: 1003

Redirect user from named route

So I have a similar issue as the person who asked this older question, except with different requirements that none of the answers there help with.

When a user opens the app, I want them to be greeted with the login page if they haven't logged in or the home page (a bottom nav bar view) if they did. I can define this in the MaterialApp as follows:

MaterialApp(
  initialRoute: authProvider.isAuthenticated
      ? '/home'
      : '/login',
  routes: {
    '/home': (_) =>
        ChangeNotifierProvider<BottomNavigationBarProvider>(
            child: AppBottomNavigationBar(),
            create: (_) => BottomNavigationBarProvider()),
    '/login': (_) => LoginView()
  },
)

So far so good. Except I want this to work on the web, and now even though the default screen when a user first opens myapp.com is myapp.com/#/login, any user can bypass the login screen by simply accessing myapp.com/#/home.

Now I tried to redirect the user to the login page in the initState() of the bottom navigation bar (and setting the initialRoute to be /home), but on mobile this has undesirable behaviour.

If I try this:

void initState() {
  super.initState();

  if (!Provider.of<AuthProvider>(context, listen: false).isAuthenticated) {
    SchedulerBinding.instance.addPostFrameCallback((_) {
      Navigator.of(context).pushNamed('/login');
    });
  }
}

then simply pressing back will return the user to the home page, again bypassing the login. If I try to use popAndPushNamed instead of just pushing, pressing back will open a blank screen (instead of closing the app).

Is there any way to do this correctly so it works on both web and mobile?

Upvotes: 1

Views: 1510

Answers (1)

Abion47
Abion47

Reputation: 24736

If you use the RouteAware mixin on your widget classes, they will be notified when they are navigated to (or away from). You can use this to check if the user is supposed to be there and to navigate them away if they are not:

To use it, first you need some global instance of RouteObserver that all your widget classes can access:

final routeObserver = RouteObserver<PageRoute>();

Then you need to register it with your MaterialApp:

MaterialApp(
  routeObservers: [routeObserver],
)

Then register your widget to the route observer:

class HomeViewState extends State<HomeView> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    routeObserver.subscribe(this, ModalRoute.of(context));
  }

  @override
  void dispose() {
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  void didPop() {
    // This gets called when this widget gets popped
  }

  void didPopNext() {
    // This gets called when another route gets popped making this widget visible
  }

  void didPush() {
    // This gets called when this widget gets pushed
  }

  void didPushNext() {
    // This gets called with another widget gets pushed making this widget hidden
  }

  ...
}

In your case, you can use the didPush route to navigate the user to the login page if they get to that page in error:

void didPush() {
  if (checkLoginStateSomehow() == notLoggedIn) {
    Navigator.of(context).pushReplacementNamed('login');
  }
}

Upvotes: 1

Related Questions