Flutter Riverpod 1.0中暴露Firebase AuthService对象应使用哪种Provider?
Flutter + Riverpod 实现Firebase手机号认证状态管理
核心思路
你现有的AuthService类状态变更默认无法被UI监听,我们通过Riverpod的Notifier封装认证状态,状态变更时自动通知UI刷新,无需额外实现监听回调。
前置要求
你已经完成Riverpod基础配置:根组件包裹ProviderScope,已引入Flutter Riverpod依赖。
步骤1:补全原有AuthService的getter
先在你提供的AuthService类中增加codeSent的对外暴露:
class AuthService { // ... 原有代码不变 // 新增这行 bool get codeSent => _codeSent; }
步骤2:定义Riverpod Provider监听状态
方案A:全状态管理(推荐)
统一管理加载、验证码已发送、验证成功、错误等全链路状态,适配绝大多数业务场景:
import 'package:riverpod/riverpod.dart'; import 'package:firebase_auth/firebase_auth.dart'; // 认证状态枚举 enum AuthStatus { initial, loading, codeSent, verified, error } class AuthNotifier extends Notifier<AuthStatus> { final FirebaseAuth _auth = FirebaseAuth.instance; String _verificationId = ''; int? _resendToken; // 错误信息可选,用于UI展示报错 String? errorMessage; @override AuthStatus build() { return AuthStatus.initial; } // 监听Firebase登录态变更 Stream<User?> authStateChanges() => _auth.authStateChanges(); User? get currentUser => _auth.currentUser; // 发送验证码 Future<void> verifyPhoneNumber(String phoneNumber) async { state = AuthStatus.loading; errorMessage = null; try { await _auth.verifyPhoneNumber( phoneNumber: phoneNumber, verificationCompleted: (PhoneAuthCredential credential) async { await _auth.signInWithCredential(credential); state = AuthStatus.verified; }, verificationFailed: (FirebaseAuthException e) { errorMessage = e.message; state = AuthStatus.error; }, codeSent: (String verificationId, int? resendToken) { _verificationId = verificationId; _resendToken = resendToken; state = AuthStatus.codeSent; }, codeAutoRetrievalTimeout: (String verificationId) { _verificationId = verificationId; }, timeout: const Duration(seconds: 60), ); } catch (e) { errorMessage = e.toString(); state = AuthStatus.error; } } // 验证码登录 Future<void> signWithOtp(String otp) async { state = AuthStatus.loading; try { final credential = PhoneAuthProvider.credential( verificationId: _verificationId, smsCode: otp, ); await _auth.signInWithCredential(credential); state = AuthStatus.verified; } on FirebaseAuthException catch (e) { errorMessage = e.message; state = AuthStatus.error; } } // 登出 Future<void> signOut() async { try { await _auth.signOut(); state = AuthStatus.initial; } catch (e) { errorMessage = e.toString(); } } } // 全局Provider final authNotifierProvider = NotifierProvider<AuthNotifier, AuthStatus>(AuthNotifier.new);
方案B:仅监听codeSent布尔状态(最小改动)
如果你只需要单独监听验证码是否发送的状态,不需要其他业务状态管理,可以用最小改动实现:
import 'package:riverpod/riverpod.dart'; class CodeSentNotifier extends Notifier<bool> { final AuthService _authService = AuthService(); @override bool build() => false; Future<void> sendVerificationCode(String phone) async { await _authService.verifyPhoneNumber(phone); state = _authService.codeSent; } } final codeSentProvider = NotifierProvider<CodeSentNotifier, bool>(CodeSentNotifier.new);
步骤3:UI层监听状态实现交互
以全状态管理方案为例,UI层直接监听Provider状态自动刷新:
import 'package:flutter_riverpod/flutter_riverpod.dart'; class PhoneAuthScreen extends ConsumerWidget { const PhoneAuthScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final authStatus = ref.watch(authNotifierProvider); final authNotifier = ref.read(authNotifierProvider.notifier); return Scaffold( body: switch(authStatus) { // 初始状态:展示手机号输入页 AuthStatus.initial => PhoneInputView( onSubmit: (phone) => authNotifier.verifyPhoneNumber(phone), ), // 加载中:展示加载动画 AuthStatus.loading => const Center(child: CircularProgressIndicator()), // 验证码已发送:展示OTP输入页,对应你需要的codeSent状态 AuthStatus.codeSent => OtpInputView( onSubmit: (otp) => authNotifier.signWithOtp(otp), ), // 验证成功:跳转到主页 AuthStatus.verified => const HomeScreen(), // 出错:展示错误提示 AuthStatus.error => ErrorTipView( msg: authNotifier.errorMessage, onRetry: () => authNotifier.verifyPhoneNumber(/* 重试手机号 */), ), } ); } }
可选:全局登录态监听
可以单独定义StreamProvider监听Firebase的登录态,自动处理登录/未登录的页面跳转:
final authStateProvider = StreamProvider<User?>((ref) { return ref.watch(authNotifierProvider.notifier).authStateChanges(); }); // 根路由页直接监听登录态 class RootPage extends ConsumerWidget { const RootPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final authState = ref.watch(authStateProvider); return authState.when( loading: () => const SplashScreen(), data: (user) => user == null ? const PhoneAuthScreen() : const HomeScreen(), error: (e, stackTrace) => const ErrorScreen(), ); } }
内容的提问来源于stack exchange,提问作者Vit Amin
相关产品推荐
相关产品推荐

