You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Riverpod状态变更时触发GoRouter redirect方法的正确实现

问题:GoRouter与Riverpod集成后,Auth状态变更时redirect不触发

问题场景与代码

我在Flutter应用中用Riverpod的Auth Provider管理登录状态:

@riverpod
class Auth extends _$Auth {
  @override
  bool build() {
    return false;
  }

  void login() {
    state = true;
  }

  void logout() {
    state = false;
  }
}

同时将GoRouter封装在Riverpod Provider中,通过ref.watch(authProvider)获取登录状态,在redirect方法中做权限校验:

@riverpod
GoRouter router(ref) {
  final isLoggedIn = ref.watch(authProvider);

  return GoRouter(
    routes: [
      GoRoute(
        path: "/",
        builder: (context, state) => const HomeScreen(),
      ),
      GoRoute(
        path: "/login",
        builder: (context, state) => LoginScreen(),
      ),
    ],
    redirect: (context, state) {
      final bool wantsToLogin = state.matchedLocation == "/login";

      if (wantsToLogin && isLoggedIn) {
        return "/";
      }

      if (!wantsToLogin && !isLoggedIn) {
        return "/login";
      }

      return null;
    },
  );
}

目前遇到的问题:ConsumerWidget中监听authProvider能正常触发组件重建,但Auth状态变更时,GoRouter的redirect方法不会自动执行。


原因分析

  1. 一次性值捕获:当前代码中isLoggedIn是创建GoRouter实例时捕获的静态值,后续Auth状态变化会触发routerProvider重建生成新的GoRouter,但现有导航栈中的GoRouter实例不会自动重新执行redirect逻辑。
  2. GoRouter的redirect触发逻辑:redirect方法默认仅在初始化导航或用户主动触发新导航操作时执行,不会主动监听外部状态变化来触发重定向。

正确集成方案

方案一:用refreshListenable绑定Auth状态监听

通过ValueNotifier创建可监听对象,绑定到GoRouter的refreshListenable参数,Auth状态变化时自动通知GoRouter重新校验redirect:

@riverpod
GoRouter router(ref) {
  // 创建可监听对象,同步Auth状态
  final refreshNotifier = ValueNotifier(ref.watch(authProvider));

  // 监听Auth状态变化,更新Notifier
  ref.listen<Auth>(authProvider, (_, next) {
    refreshNotifier.value = next;
  });

  return GoRouter(
    refreshListenable: refreshNotifier,
    routes: [
      GoRoute(
        path: "/",
        builder: (context, state) => const HomeScreen(),
      ),
      GoRoute(
        path: "/login",
        builder: (context, state) => LoginScreen(),
      ),
    ],
    redirect: (context, state) {
      // 直接读取最新Auth状态
      final isLoggedIn = ref.read(authProvider).state;
      final wantsToLogin = state.matchedLocation == "/login";

      if (wantsToLogin && isLoggedIn) return "/";
      if (!wantsToLogin && !isLoggedIn) return "/login";
      return null;
    },
  );
}

方案二:监听Auth状态主动刷新GoRouter

在routerProvider中监听Auth状态变化,主动调用GoRouter的refresh()方法触发redirect重校验:

@riverpod
GoRouter router(ref) {
  final router = GoRouter(
    routes: [
      GoRoute(
        path: "/",
        builder: (context, state) => const HomeScreen(),
      ),
      GoRoute(
        path: "/login",
        builder: (context, state) => LoginScreen(),
      ),
    ],
    redirect: (context, state) {
      final isLoggedIn = ref.read(authProvider).state;
      final wantsToLogin = state.matchedLocation == "/login";

      if (wantsToLogin && isLoggedIn) return "/";
      if (!wantsToLogin && !isLoggedIn) return "/login";
      return null;
    },
  );

  // Auth状态变化时主动刷新GoRouter
  ref.listen<Auth>(authProvider, (_, __) {
    router.refresh();
  });

  return router;
}

注意:redirect方法中使用ref.read()而非ref.watch(),因为redirect不属于组件build生命周期,避免不必要的监听绑定。


内容的提问来源于stack exchange,提问作者Leonardo Mantovani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:35:16