修复优化引起的 demo bug

This commit is contained in:
Cody
2026-03-08 21:38:25 +08:00
parent 9610c455ec
commit 132349c410
2 changed files with 19 additions and 6 deletions

View File

@@ -59,6 +59,10 @@ class LoginViewModel extends _$LoginViewModel {
/// 仅用于演示路由守卫行为,正式 UI 就绪后删除此方法,改用 [login]。
/// 正式 [login] 成功后同样需要调用 [AuthNotifier.login] 更新守卫状态。
Future<void> demoLogin() async {
// 防止连点重入:第一次调用未完成前忽略后续调用
if (state.isLoading) return;
state = state.copyWith(isLoading: true, error: null);
try {
final storageApi = ref.read(storageSdkProvider);
final storageLifeCycle = storageApi as StorageSdkLifecycle;
@@ -72,11 +76,15 @@ class LoginViewModel extends _$LoginViewModel {
// 先完成 DB 操作,再标记登录状态(失败时不会误标为已登录)
await storageLifeCycle.openDatabase(user.uid);
final userCompanion = UserDto.fromEntity(user).toCompanion();
await storageApi.insert(userCompanion);
await storageApi.insertOrReplace(userCompanion);
// 全部成功后再更新登录状态,触发路由守卫重定向
// 注意login() 触发导航后 provider 随即被 dispose之后不能再写 state
if (!ref.mounted) return;
ref.read(authNotifierProvider).login();
} catch (e) {
// 导航已发生时 provider 已被 dispose静默丢弃不再写 state
if (!ref.mounted) return;
state = state.copyWith(error: e.toString(), isLoading: false);
}
}
@@ -94,15 +102,19 @@ class LoginViewModel extends _$LoginViewModel {
.read(loginUseCaseProvider)
.execute(email: email, password: password);
if (!ref.mounted) return;
state = state.copyWith(user: user, isLoading: false);
} on FormatException catch (e) {
// 格式校验失败UseCase 层抛出)
if (!ref.mounted) return;
state = state.copyWith(error: e.message, isLoading: false);
} on ApiError catch (e) {
// 网络 / 服务端错误Repository → SDK 透传)
if (!ref.mounted) return;
state = state.copyWith(error: e.displayMessage, isLoading: false);
} catch (e) {
// 兜底:防止未预期的异常导致 isLoading 死锁
if (!ref.mounted) return;
state = state.copyWith(error: e.toString(), isLoading: false);
}
}

View File

@@ -15,13 +15,12 @@ class LoginPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch 保持 loginViewModelProvider 存活AutoDispose 需要至少一个监听者)
final state = ref.watch(loginViewModelProvider);
final s = context.styles;
return Scaffold(
appBar: AppBar(
title: const Text('登录'),
automaticallyImplyLeading: false,
),
appBar: AppBar(title: const Text('登录'), automaticallyImplyLeading: false),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -34,7 +33,9 @@ class LoginPage extends ConsumerWidget {
),
const SizedBox(height: 32),
FilledButton(
onPressed: () => ref.read(loginViewModelProvider.notifier).demoLogin(),
onPressed: state.isLoading
? null
: () => ref.read(loginViewModelProvider.notifier).demoLogin(),
child: const Text('登录'),
),
],