Flutter Cubit状态更新滞后:启动页获取本地应用信息为空解决方案咨询
问题与解决方案
需求说明
用户未认证时,需从状态中获取Hive存储的本地应用信息,并执行对应路由跳转:
- 若用户已访问过应用,跳转至登录页
- 若用户首次访问,跳转至引导页
问题现状
调用getLocalAppInfo()后,启动页获取到的值始终为初始状态的null,原因是异步加载未完成时就读取了状态。已尝试以下方案但未解决:
- 在main函数、页面initState中调用
getLocalAppInfo() - 使用FutureBuilder处理异步状态
- 在AuthCubit中新增
getAuthState()方法,但该方法必须添加1秒延迟才能触发监听器,且逻辑存在错误
相关代码
LocalAppInfoCubit代码
@injectable class LocalAppInfoCubit extends Cubit<LocalAppInfoState> { final UserRepository _userRepository; LocalAppInfoCubit(this._userRepository) : super(const LocalAppInfoState(null, null)); Future<void> getLocalAppInfo() async { var result = await _userRepository.getLocalAppInfo(); result.fold((l) { emit(LocalAppInfoState(null, l)); }, (r) { emit(LocalAppInfoState(r, null)); }); } void updateLocalAppInfo(LocalAppInfo localAppInfo) { _userRepository.updateLocalAppInfo(localAppInfo: localAppInfo); emit(LocalAppInfoState(localAppInfo, null)); } }
启动页代码
if (state is UnauthenticatedState) { final localAppInfoState = context.read<LocalAppInfoCubit>().state; if (localAppInfoState.localAppInfo != null) { if (localAppInfoState.localAppInfo!.hisVisited) { //checking it the user is visiting first time context.pushRoute(const LoginRoute()); } else { context.pushRoute(const OnboardingMainRoute()); } } else if (localAppInfoState.appError != null) { print("error"); } else { print("null"); } }
尝试过的FutureBuilder代码
FutureBuilder( future: context.read<AuthCubit>().getAuthState(), builder: (context, snapshot) { if (snapshot.hasData) { if (snapshot.data is AuthenticatedState) { // context.pushRoute(const HomeRoute()); return Center( child: Text("auth"), ); } else if (snapshot.data is UnauthenticatedState) { // final localAppInfoState = context.read<LocalAppInfoCubit>().state; // if (localAppInfoState.localAppInfo != null) { // if (localAppInfoState.localAppInfo!.hisVisited) { // //checking it the user is visiting first time // context.pushRoute(const LoginRoute()); // } else { // context.pushRoute(const OnboardingMainRoute()); // } // } return Center( child: Text("Unauth"), ); } else { return Center( child: Text("Null"), ); } } return Center( child: Text("Error"), ); }, ),
AuthCubit新增的getAuthState方法
Future<AuthState> getAuthState() async { var result = await _userRepository.getUserInfo(); late UserInfo? userInfo; result.fold((l) { return AuthErrorState(l); }, (r) async { await Future.delayed(const Duration( seconds: 1)); //the listner is not working without this.Have to correct it userInfo = r; if (userInfo == null) { return const UnauthenticatedState(); } else if (userInfo!.isAuthenticated) { return const AuthenticatedState(); } else { return const UnauthenticatedState(); } }); return const UnauthenticatedState(); } }
解决方案
1. 修复AuthCubit的getAuthState方法
当前方法存在逻辑错误:fold中的返回值不会被外层接收,最后总是返回UnauthenticatedState。修改为正确的异步处理:
Future<AuthState> getAuthState() async { final result = await _userRepository.getUserInfo(); return result.fold( (failure) => AuthErrorState(failure), (userInfo) { if (userInfo == null) { return const UnauthenticatedState(); } else if (userInfo.isAuthenticated) { return const AuthenticatedState(); } else { return const UnauthenticatedState(); } }, ); }
移除不必要的Future.delayed,延迟是之前逻辑错误导致状态未正确emit,修复后无需延迟。
2. 确保LocalAppInfoCubit提前加载数据
在应用启动时初始化并调用getLocalAppInfo(),确保数据在启动页渲染前开始加载:
void main() async { WidgetsFlutterBinding.ensureInitialized(); // 初始化Hive等必要依赖 await Hive.initFlutter(); final injector = await configureDependencies(); final localAppInfoCubit = injector<LocalAppInfoCubit>(); await localAppInfoCubit.getLocalAppInfo(); // 提前加载本地信息 runApp( BlocProvider.value( value: localAppInfoCubit, child: const MyApp(), ), ); }
3. 同时监听Auth和LocalAppInfo的状态变化
在启动页使用BlocConsumer,等待两个状态都准备好后再处理路由跳转,避免读取未更新的初始状态:
BlocConsumer<AuthCubit, AuthState>( listener: (context, authState) { if (authState is UnauthenticatedState) { // 监听LocalAppInfoCubit的状态变化 context.read<LocalAppInfoCubit>().stream.listen((localState) { if (localState.localAppInfo != null) { if (localState.localAppInfo!.hisVisited) { context.pushRoute(const LoginRoute()); } else { context.pushRoute(const OnboardingMainRoute()); } } else if (localState.appError != null) { print("本地信息加载失败: ${localState.appError}"); } }); } else if (authState is AuthenticatedState) { context.pushRoute(const HomeRoute()); } }, builder: (context, authState) { // 显示加载状态 return const Center(child: CircularProgressIndicator()); }, )
4. 优化LocalAppInfoCubit的初始状态
将初始状态设置为加载中,避免直接显示null:
// 定义状态时增加加载状态 sealed class LocalAppInfoState {} class LocalAppInfoLoading extends LocalAppInfoState {} class LocalAppInfoLoaded extends LocalAppInfoState { final LocalAppInfo? data; final AppError? error; LocalAppInfoLoaded(this.data, this.error); } // 修改Cubit初始化 LocalAppInfoCubit(this._userRepository) : super(LocalAppInfoLoading()); // 修改getLocalAppInfo方法 Future<void> getLocalAppInfo() async { emit(LocalAppInfoLoading()); var result = await _userRepository.getLocalAppInfo(); result.fold( (l) => emit(LocalAppInfoLoaded(null, l)), (r) => emit(LocalAppInfoLoaded(r, null)), ); }
这样在启动页可以根据加载状态显示loading,避免读取null值。
内容的提问来源于stack exchange,提问作者Suraj Rao
相关产品推荐
相关产品推荐

