csells / go_router

The purpose of the go_router for Flutter is to use declarative routes to reduce complexity, regardless of the platform you're targeting (mobile, web, desktop), handling deep linking from Android, iOS and the web while still allowing an easy-to-use developer experience.
https://gorouter.dev
441 stars 97 forks source link

Redirect not working correctly on web #47

Closed doughywilson closed 3 years ago

doughywilson commented 3 years ago

I am having an issue with the redirect not working correctly (or maybe I am using it wrong?). In the code below, I simply force a redirect to the "/login" screen every time. However, I get the error page.

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);
  late final _router = GoRouter(
    routes: [
      GoRoute(
        path: "/",
        name: "home",
        pageBuilder: (context, state) => MaterialPage<void>(
          key: state.pageKey,
          child: const Scaffold(
            body: Text("Home page"),
          ),
        ),
      ),
      GoRoute(
        path: "/login",
        name: "login",
        pageBuilder: (context, state) => MaterialPage<void>(
          key: state.pageKey,
          child: const Scaffold(
            body: Text("Login page"),
          ),
        ),
      ),
    ],
    errorPageBuilder: (context, state) => MaterialPage<void>(
      key: state.pageKey,
      child: const Scaffold(
        body: Text("Page not found"),
      ),
    ),
    redirect: (state) {
      return "/login";
    },
  );

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      routeInformationParser: _router.routeInformationParser,
      routerDelegate: _router.routerDelegate,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
    );
  }
}
csells commented 3 years ago

you need to set a refreshListenable as per the README

doughywilson commented 3 years ago

Is that required? I can't seem to find where in the documentation is says it's required, and it shows and example of using the redirect without refreshListenable. Even so, I tried adding a refreshListenable and still get the same behavior.

csells commented 3 years ago

You're right -- you don't have to use a refreshListenable if you're manually navigating.

You've got a redirect loop:

Exception: Redirect loop detected: / => /login => /login

You can see it if you show the exception in your error page:

errorPageBuilder: (context, state) => MaterialPage<void>(
  key: state.pageKey,
  child:  Scaffold(
    body: Text(state.error.toString()),
  ),
),

Redirection is called repeatedly until a null is returned. This allows for multiple route redirections, etc. before landing at the desired page, which in turn allows for routes in an app to build up over time, keeping the old routes but redirecting them to the new ones.

doughywilson commented 3 years ago

Oh I see, thanks! I like that. This would be super helpful to point out directly in the documentation. I didn't realize that the redirect function would run until it returns null.

csells commented 3 years ago

Ha. Way ahead of you. The README in the repo is already updated and it'll be updated on pub.dev on the next release. The next release will also include exception info in the debug output in the case that you've got route debugging turned on. Your questions and feedback have been super helpful.

How's your use case? Are things working they way you want?

doughywilson commented 3 years ago

Sweet!

I'm now trying to figure out how to initialize my app correctly. I'm having some complications when it comes to when/how to load a stored api key BEFORE the go_router attempts to run the redirect function and begin routing. It's tricky because the loading of the api key is asynchronous.

I'm also having this really annoying issue of not being able to hot reload my app. Whenever I make changes, the screen goes white and refuses to come back, even if I manually refresh the browser. Not ready to blame that on go_router though.

It would be cool if go_router had some kind of built in paradigm for initializing auth state to avoid these async/init headaches. Not sure if that's possible though.

csells commented 3 years ago

You can do async init before even running your app, e.g.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

Of course, you can bake auth init into how you route, too, and use it as input for redirection.

doughywilson commented 3 years ago

Yeah, tried that strategy but still having some hangups. Maybe I'm screwing something up though.