如何定义依赖于Provider的GoRouter?
我正在将 GoRouter 集成到我的 Flutter 应用程序中,我已经在使用 Riverpod。我有一个 isAuthorizedProvider 定义如下:
final isAuthorizedProvider = Provider<bool>((ref) {
final authStateChanged = ref.watch(_authStateChangedProvider);
final user = authStateChanged.asData?.value;
return user != null;
});
而且我不确定如何定义依赖于上面的 Provider 的 GoRouter 。我想出了以下几点:
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,
);
}
这是正确的方法吗?
I'm integrating GoRouter in my Flutter app where I'm already using Riverpod. I have an isAuthorizedProvider
defined as follows:
final isAuthorizedProvider = Provider<bool>((ref) {
final authStateChanged = ref.watch(_authStateChangedProvider);
final user = authStateChanged.asData?.value;
return user != null;
});
And I'm not sure how to define a GoRouter that depends on the Provider above. I've come up with the following:
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,
);
}
Is this the right way to do it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我不认为您应该
在重定向块内调用此行,因为这将导致整个 GoRouter 实例重建(并且您将丢失整个导航堆栈)。
我就是这样做的:
请注意,此代码是非反应性,因为它会在 authState 更改时刷新路由器。因此,与此相结合,您需要在登录/注销时执行显式导航事件。
或者,您可以使用
refreshListenable
参数。I don't thing you should be calling this line
inside the redirect block, because that will cause your entire
GoRouter
instance to rebuild (and you'll lose the entire nav stack).This is how I've done it:
Note that this code is not reactive in the sense that it will refresh the router when the authState changes. So in combination with this, you need to perform an explicit navigation event when you sign-in/sign-out.
Alternatively, you can use the
refreshListenable
argument.您可以使用重定向来完成此操作,但是我想出了一种使用
navigatorBuilder
的方法。通过这种方式,您可以保持原始导航器状态(您将被重定向回您最初在网络上访问或通过深层链接访问的页面),并且不必不断重建整个路由器。navigatorBuilder
基本上允许您在 MaterialApp 和 Navigator 之间注入一些小部件。我们使用 Riverpod 的消费者小部件来访问 ref,然后不必重建整个路由器,并且我们可以使用 ref 访问身份验证状态。在我的示例中,
ref.watch(authControllerProvider)
返回一个AsyncValue
,因此如果用户已登录,我们将返回child
code> (当前导航路线),如果他们已注销,则向他们显示登录屏幕,如果他们正在加载,我们可以显示加载屏幕等。如果您想根据角色重定向用户(例如,只有管理员可以看到管理仪表板) ,那么该逻辑应该进入重定向函数使用 @bizz84 描述的可监听对象。
You can do it this way using redirect, however I've come up with a way that uses
navigatorBuilder
. This way you maintain the original navigator state (you will be redirected back to whichever page you originally visited on web or with deep linking), and the whole router doesn't have to be constantly be rebuilt.navigatorBuilder
basically allows you to inject some widget between the MaterialApp and the Navigator. We use Riverpod's consumer widget to access the ref and then the whole router doesn't have to be rebuilt, and we can access auth state using the ref.In my example,
ref.watch(authControllerProvider)
returns anAsyncValue<AuthUser?>
, so if the user is logged in, we return thechild
(current navigated route), if they are logged out, show them login screen, and if they are loading we can show a loading screen etc.If you want to redirect users based on roles (e.g. only admin can see admin dashboard), then that logic should go into the redirect function using a listenable as @bizz84 described.