Flutter:如何使用GoRouter和Bloc从启动屏幕导航到登录屏幕

5

最近我在我的应用程序中切换到Go路由器,因为它非常容易实现。但是我在从启动屏幕移动到登录屏幕时遇到了麻烦。我在我的启动屏幕中有一个逻辑,在其中检查用户是否已经登录。根据用户的身份验证,屏幕会进入登录屏幕或主页。

这是启动屏幕。

    class SplashScreen extends StatefulWidget {
  static const routeName = "/SplashScreen";

  const SplashScreen({Key? key}) : super(key: key);

  @override
  _SplashScreenState createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen>
    with SingleTickerProviderStateMixin {
  @override
  Widget build(BuildContext context) {
    return BlocConsumer<AuthenticationBloc, AuthenticationState>(
      listener: (context, state) {
        if (kDebugMode) {
          print('Listener: $state');
        }
        Future.delayed(const Duration(seconds: 3), () {
          if (state.authStatus == AuthStatus.unAuthenticated) {
            GoRouter.of(context).go('/login');

            Navigator.pushNamed(context, SignUpScreen.routeName);
          } else if (state.authStatus == AuthStatus.authenticated) {
            //Navigator.popUntil(context, (route) => route.isFirst);
            Navigator.pushReplacementNamed(context, HomePage.routeName);
          }
        });
      },
      builder: (context, Object? state) {
        if (kDebugMode) {
          print('object: $state');
        }
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Text(
                  "Welcome to Musajjal",
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
                ),
                const SizedBox(
                  height: 20,
                ),
                Image.asset(
                  'assets/musajjalBlue.png',
                  width: 300,
                  height: 300,
                ),
                const SizedBox(
                  height: 20,
                ),
                const Text(
                  "Hifz ul Quran Records",
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
                ),
                const SizedBox(
                  height: 20,
                ),
                const CircularProgressIndicator(
                  color: Colors.blueGrey,
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

接下来是我的Go路由器函数

GoRouter _router(AuthenticationBloc bloc) {
return GoRouter(
  routes: <GoRoute>[
    GoRoute(
      path: '/',
      builder: (context, state) => const SplashScreen(),
      routes: <GoRoute>[
        GoRoute(path: 'login', builder: (context, state) => LoginScreen()),
        GoRoute(
            path: 'signUp', builder: (context, state) => SignUpScreen()),
        GoRoute(path: 'homePage', builder: (context, state) => HomePage())
      ],
      redirect: (BuildContext context, GoRouterState state) {
        final isLoggedIn =
            bloc.state.authStatus == AuthStatus.authenticated;
        final isLoggingIn = state.location == '/login';
        print(isLoggedIn);

        if (!isLoggedIn && !isLoggingIn) return '/login';
        if (isLoggedIn && isLoggingIn) return '/homePage';
        return null;
      },
    ),
  ],
);

问题是应用程序停留在启动画面上,并且没有进入到登录界面。请帮忙解决。

3个回答

2

尝试以下代码:

main.dart

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
        providers: [
          BlocProvider<AuthenticationBloc>(
            create: (context) => AuthenticationBloc),  Specify the BlocProvider here
          ),
        ],
        child: MaterialApp(
            theme: customTheme(context),
            debugShowCheckedModeBanner: false,
            routerConfig: router,
  }

router.dart

final GoRouter router = GoRouter(routes: [
  GoRoute(
      path: "/",
      builder: (context, state) {
        return BlocBuilder<AuthCubit, AuthState>(
          buildWhen: (oldState, newState) {
            return oldState is AuthInitialState;
           },
          builder: (context, state) {
             if (state is AuthLoading) {
              // return const SplashScreen();           // alternative way
               context.goNamed(SplashScreen.routeName);  Display your splash screen here and you can provide delay while changing state in your bloc
            }
            else if (state is AuthenticationUnauthenticated) {
               context.goNamed(LoginPage.routeName);  
            } else if (state is Authenticated) {
                context.goNamed(HomePage.routeName);           
             } else {
              return const Scaffold();
            }
          },
        );
      }),

1

根据文档,使用redirect参数可以为您的应用程序添加重定向机制。

使用go_router实现重定向有两种方式:

  • 在顶层(顶层重定向),这意味着每当您的应用程序进行导航时,将调用redirect函数来查看您的应用程序是否导航到所请求的页面,或者重定向到您选择的其他内容(您可以选择所需的逻辑);此重定向在GoRouter构造函数中定义。
  • 在路由级别(路由级别重定向),这在GoRoute构造函数中定义,因此仅在特定路由导航上,而不是在顶层重定向中的任何导航事件上。

对于您的情况,从启动屏幕导航到登录屏幕或主屏幕,您可以考虑使用顶层重定向方式,通过检索应用程序的身份验证状态来实现。

  • 如果用户已经通过身份验证,您将重定向到主页(这意味着在redirect参数中返回null,如文档中所述)
  • 如果用户未经身份验证,您将重定向到登录页面以供用户登录
  • 如果您尚不确定用户是否已经通过身份验证,因为您仍在获取此信息,您将显示闪屏页面(您可以想象使用AuthenticationStatus枚举来表示三个值:已通过身份验证、未经身份验证和未知,就像在为Bloc包制作的登录实现示例中所做的那样(链接在这里

我在这里遇到的一个问题是,当触发导航事件时,会调用redirect函数,但我们通常希望根据用户的身份验证状态自动从闪屏页面转到登录页面或主页。 您可以查看我之前提到的Bloc包教程,它提供了许多实现这一点的提示。我目前正在尝试使用GoRouter做同样的事情。

编辑(2023年9月12日):

我想出了以下解决方案:
```dart class MyFirstPage extends StatelessWidget { const MyFirstPage({super.key});
static String routePath = '/';
@override Widget build(BuildContext context) { return BlocListener( listener: (context, state) { switch (state.status) { case AuthenticationStatus.unauthenticated: context.go('/login'); break; case AuthenticationStatus.authenticated: context.go('/home'); break; case AuthenticationStatus.unknown: break; } }, child: const SplashPage(), ); } } ```
使用以下GoRouter配置:
final router = GoRouter(
    navigatorKey: _rootNavigatorKey,
    initialLocation: MyFirstPage.routePath,
    redirect: (context, state) {
      var authStatus = context.read<AuthenticationBloc>().state.status;

      if (authStatus == AuthenticationStatus.unknown) {
        return MyFirstPage.routePath;
      } else if (authStatus == AuthenticationStatus.unauthenticated) {
        return LoginPage.routePath;
      } else {
        return null;
      }
    },
    routes: [
      GoRoute(
        path: MyFirstPage.routePath,
        builder: (_, __) => const MyFirstPage(),
      ),
      GoRoute(
        name: SplashPage.routeName,
        path: SplashPage.routePath,
        builder: (_, __) => const SplashPage(),
      ),
      GoRoute(
        name: LoginPage.routeName,
        path: LoginPage.routePath,
        builder: (_, __) => LoginPage(),
      ),
      GoRoute(
        name: HomePage.routeName,
        path: HomePage.routePath,
        builder: (_, __) => const HomePage(),
      ),
    ],
    });

解释:

  • 应用程序的起始页面是MyFirstPage,在获取用户的身份验证状态时显示我的SplashPage。
  • 如果用户未经身份验证(AuthenticationStatus.unauthenticated),我们将用户重定向到LoginPage(context.go('/login')
  • 如果用户已经通过身份验证(AuthenticationStatus.authenticated),我们将用户重定向到HomePage(context.go('/home')
  • 如果我们无法获取身份验证状态(AuthenticationStatus.unknown),我们不做任何操作,因为我们已经在SplashPage上,并且我们等待直到我们获取到AuthStatus(在这里,直到AuthenticationBloc的状态被更新)
  • GoRouter构造函数的redirect参数中,我们的做法是在任何导航事件发生时检查用户是否仍然经过身份验证。我们通过检查他们的身份验证状态来实现:如果状态更改为AuthenticationStatus.unauthenticated,我们将用户重定向到登录页面;如果状态更改为Authentication.unknown,我们将用户重定向到显示SplashScreen并等待用户身份验证状态更新的MyFirstPage(我没有进行详尽的测试以保证它的正确性)

0

尝试更改重定向中的逻辑

if (!isLoggedIn && !isLoggingIn) return '/login';
if (isLoggedIn) return '/homePage';

此外,考虑将登录逻辑设置为OR而不是AND -- 可选。
if (!isLoggedIn || !isLoggingIn) return '/login';

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接