首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何定义依赖于提供程序的GoRouter?

如何定义依赖于提供程序的GoRouter?
EN

Stack Overflow用户
提问于 2022-03-01 11:24:41
回答 2查看 2.5K关注 0票数 4

我正在我的颤振应用程序中集成GoRouter,我已经在使用Riverpod了。我有一个isAuthorizedProvider定义如下:

代码语言:javascript
复制
final isAuthorizedProvider = Provider<bool>((ref) {
  final authStateChanged = ref.watch(_authStateChangedProvider);
  final user = authStateChanged.asData?.value;
  return user != null;
});

我也不知道如何定义依赖于上面的提供者的GoRouter。我想出了以下几点:

代码语言:javascript
复制
final goRouterProvider = Provider<GoRouter>((ref) => GoRouter(
      debugLogDiagnostics: true,
      redirect: (state) {
        final isAuthorized = ref.watch(isAuthorizedProvider);
        final isSigningIn = state.subloc == state.namedLocation('sign_in');

        if (!isAuthorized) {
          return isSigningIn ? null : state.namedLocation('sign_in');
        }

        // if the user is logged in but still on the login page, send them to
        // the home page
        if (isSigningIn) return '/';

        // no need to redirect at all
        return null;
      },
      routes: [
        GoRoute(
          path: '/',
          ...,
        ),
        GoRoute(
          name: 'sign_in',
          path: '/sign_in',
          ...,
        ),
        GoRoute(
            name: 'main',
            path: '/main',
            ...,
        ),
        ...
      ],
    ));

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final goRouter = ref.watch(goRouterProvider);
    return MaterialApp.router(
      routeInformationParser: goRouter.routeInformationParser,
      routerDelegate: goRouter.routerDelegate,
    );
  }

这样做对吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-03-17 16:38:00

我不知道你不该打这条电话

代码语言:javascript
复制
ref.watch(isAuthorizedProvider);

在重定向块中,因为这将导致整个GoRouter实例重新构建(您将丢失整个nav堆栈)。

我就是这样做的:

代码语言:javascript
复制
class AppRouterListenable extends ChangeNotifier {
  AppRouterListenable({required this.authRepository}) {
    _authStateSubscription =
        authRepository.authStateChanges().listen((appUser) {
      _isLoggedIn = appUser != null;
      notifyListeners();
    });
  }
  final AuthRepository authRepository;
  late final StreamSubscription<AppUser?> _authStateSubscription;
  var _isLoggedIn = false;
  bool get isLoggedIn => _isLoggedIn;

  @override
  void dispose() {
    _authStateSubscription.cancel();
    super.dispose();
  }
}

final appRouterListenableProvider =
    ChangeNotifierProvider<AppRouterListenable>((ref) {
  final authRepository = ref.watch(authRepositoryProvider);
  return AppRouterListenable(authRepository: authRepository);
});

final goRouterProvider = Provider<GoRouter>((ref) {
  final authRepository = ref.watch(authRepositoryProvider);
  final appRouterListenable =
      AppRouterListenable(authRepository: authRepository);
  return GoRouter(
    debugLogDiagnostics: false,
    initialLocation: '/',
    redirect: (state) {
      if (appRouterListenable.isLoggedIn) {
        // on login complete, redirect to home
        if (state.location == '/signIn') {
          return '/';
        }
      } else {
        // on logout complete, redirect to home
        if (state.location == '/account') {
          return '/';
        }
        // TODO: Only allow admin pages if user is admin (#125)
        if (state.location.startsWith('/admin') ||
            state.location.startsWith('/orders')) {
          return '/';
        }
      }
      // disallow card payment screen if not on web
      if (!kIsWeb) {
        if (state.location == '/cart/checkout/card') {
          return '/cart/checkout';
        }
      }
      return null;
    },
    routes: [],
  );
}

请注意,这段代码是,而不是反应性,因为当authState更改时,它会刷新路由器。因此,结合这一点,您需要在签入/退出时执行显式导航事件。

或者,您可以使用refreshListenable参数。

票数 2
EN

Stack Overflow用户

发布于 2022-08-02 10:18:55

您可以使用重定向这样做,但是我想出了一种使用navigatorBuilder的方法。通过这种方式,您可以维护原始的导航器状态(您将被重定向回您最初在web上访问的页面或深度链接的页面),并且不必不断地重建整个路由器。

代码语言:javascript
复制
final routerProvider = Provider((ref) {
  return GoRouter(
    routes: [
      GoRoute(
        path: '/',
        builder: (context, state) => const OrdersScreen(),
      ),
      GoRoute(
        path: '/login',
        builder: (context, state) => const AuthScreen(),
      ),
    ],
    navigatorBuilder: (context, state, child) {
      return Consumer(
        builder: (_, ref, __) =>
          ref.watch(authControllerProvider).asData?.value == null
            ? Navigator(
                onGenerateRoute: (settings) => MaterialPageRoute(
                  builder: (context) => AuthScreen(),
                ),
              )
            : child,
      );
    },
  );
});

navigatorBuilder基本上允许您在MaterialApp和Navigator之间注入一些小部件。我们使用Riverpod的使用者部件来访问ref,然后不必重建整个路由器,我们可以使用ref访问auth状态。

在我的示例中,ref.watch(authControllerProvider)返回一个AsyncValue<AuthUser?>,所以如果用户登录,我们返回child (当前导航路由),如果他们被注销,显示登录屏幕,如果他们正在加载,我们可以显示一个加载屏幕等等。

如果您想要根据角色重定向用户(例如,只有admin才能看到管理仪表板),那么该逻辑应该使用所描述的listenable @bizz84 84进入重定向函数。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71307743

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档