Flutter实现FirebaseAuth登录错误时显示Snackbar/Dialog
解决FirebaseAuth认证错误在UI层显示Snackbar/Dialog的问题
要实现认证错误的前端提示,需要从状态存储错误信息、错误向上传递、UI监听状态触发提示三个环节改造现有代码,具体步骤如下:
1. 扩展SigninState,新增错误信息字段
原状态仅标记错误状态,无法传递具体错误内容,需要为SigninState添加errorMessage字段,用于存储Firebase返回的错误描述:
part of 'signin_cubit.dart'; enum SigninStatus { initial, submitting, success, error } class SigninState extends Equatable { final String email; final String password; final SigninStatus status; final String? errorMessage; // 新增:存储具体错误信息 bool get isFormValid => email.isNotEmpty && password.isNotEmpty; const SigninState({ required this.email, required this.password, required this.status, this.errorMessage, }); factory SigninState.initial() { return SigninState( email: '', password: '', status: SigninStatus.initial, errorMessage: null, ); } @override bool get stringify => true; @override List<Object?> get props => [email, password, status, errorMessage]; // 更新props包含新字段 SigninState copyWith({ String? email, String? password, SigninStatus? status, String? errorMessage, }) { return SigninState( email: email ?? this.email, password: password ?? this.password, status: status ?? this.status, errorMessage: errorMessage ?? this.errorMessage, ); } }
2. 修改AuthRepository,抛出错误而非仅打印
原认证函数仅打印错误,需要将错误向上抛出,让Cubit捕获并处理:
Future<User?> signInWithEmailAndPassword({required String email, required String password}) async { try { final credential = await _firebaseAuth.signInWithEmailAndPassword( email: email, password: password); final user = _userFromFirebaseUser(credential.user); return user; } catch (e) { rethrow; // 抛出错误,交给上层Cubit处理 } }
3. 更新SigninCubit,捕获并存储错误信息
在signInWithCredentials方法中,捕获Firebase错误并提取具体信息,更新到状态中:
void signInWithCredentials() async { try { emit(state.copyWith(status: SigninStatus.submitting)); // 新增提交中状态,优化用户感知 await _authRepository.signInWithEmailAndPassword( email: state.email, password: state.password); emit( state.copyWith(status: SigninStatus.success, errorMessage: null), ); } catch (e) { // 提取Firebase认证错误信息,默认提示兜底 String errorMsg = e is FirebaseAuthException ? e.message ?? '登录失败,请重试' : '登录失败,请重试'; emit(state.copyWith(status: SigninStatus.error, errorMessage: errorMsg)); } }
4. 在UI页面监听状态,触发Snackbar/Dialog
在输入框所在页面,使用BlocListener监听SigninState的变化,当状态变为error时,弹出提示:
// 在页面的Widget树中添加BlocListener BlocListener<SigninCubit, SigninState>( listener: (context, state) { // 显示错误提示 if (state.status == SigninStatus.error && state.errorMessage != null) { // 方案1:显示Snackbar ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.errorMessage!), backgroundColor: Colors.red, duration: const Duration(seconds: 3), ), ); // 方案2:显示Dialog(二选一即可) // showDialog( // context: context, // builder: (ctx) => AlertDialog( // title: const Text('登录失败'), // content: Text(state.errorMessage!), // actions: [ // TextButton( // onPressed: () => Navigator.pop(ctx), // child: const Text('确定'), // ), // ], // ), // ); } // 登录成功后的跳转逻辑 if (state.status == SigninStatus.success) { Navigator.pushReplacementNamed(context, '/home'); // 替换为你的主页路由 } }, child: // 原页面内容(输入框、按钮等) )
同时可以优化按钮点击逻辑,先校验表单完整性:
if (tabController.index == 3) { final signinState = context.read<SigninCubit>().state; if (signinState.isFormValid) { context.read<SigninCubit>().signInWithCredentials(); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请填写完整的邮箱和密码')), ); } }
内容的提问来源于stack exchange,提问作者Ruder Buster
相关产品推荐
相关产品推荐

