我正在我的颤振应用程序中集成GoRouter,我已经在使用Riverpod了。我有一个isAuthorizedProvider定义如下:
final isAuthorizedProvider = Provider<bool>((ref) {
final authStateChanged = ref.watch(_authStateChangedProvider);
final user = authStateChanged.asData?.value;
return user != null;
});我也不知道如何定义依赖于上面的提供者的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,
);
}这样做对吗?
发布于 2022-03-17 16:38:00
我不知道你不该打这条电话
ref.watch(isAuthorizedProvider);在重定向块中,因为这将导致整个GoRouter实例重新构建(您将丢失整个nav堆栈)。
我就是这样做的:
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参数。
发布于 2022-08-02 10:18:55
您可以使用重定向这样做,但是我想出了一种使用navigatorBuilder的方法。通过这种方式,您可以维护原始的导航器状态(您将被重定向回您最初在web上访问的页面或深度链接的页面),并且不必不断地重建整个路由器。
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进入重定向函数。
https://stackoverflow.com/questions/71307743
复制相似问题