riverpod_firebase_phone_auth

一个展示如何使用 Firebase 和 Riverpod 处理移动身份验证的 Flutter 项目模板。

使用的软件包

  • Freezed – 用于生成处理各种 AuthStates 的联合类型
  • GoogleFonts – 轻松使用 fonts.google.com 中的任何字体
  • responsive_sizer – 用于尺寸调整
  • flutter_typeahead – 用于实现国家选择器
  • country_list_pick – 用于国家选择器的国旗图标和名称
  • firebase_core 和 firebase_auth

关于

该项目主要使用 freezed 联合类型来实现大多数移动登录应用程序使用的各种状态。
如果您不了解 freezed,请查看该包的 freezed 文档。作者有很棒的文档,然后找到一个用例并继续冻结。

因此,在通过手机注册时,注册手机可能会处于几种状态。
– 初始状态,例如欢迎屏幕
– 用户输入电话号码的页面。
– 加载中,即提示输入短信验证码/检查短信验证码是否有效
– 短信验证码已发送到用户的设备,即显示验证码输入屏幕
– 短信验证已通过。
– 短信验证码无效。
– 错误 – 发生了其他错误

该模板主要做的是使用联合类型处理移动注册过程中可能发生的各种状态,然后将状态映射到相应的 widget。

各种身份验证状态

@freezed
class AuthState with _$AuthState {
/// Welcome page/User has not entered the phone number
const factory AuthState.initial() = _Initial;

/// When something is happening. 
const factory AuthState.loading(String msg) = _Loading;

/// when waiting for user to enter phone number
const factory AuthState.waitingForUserInput() = _WaitingForUserInput;

/// when the code has been sent to the user
const factory AuthState.codeSent(String verificationId) = _CodeSent;

/// when the user successfully signs in
const factory AuthState.gotFirebaseUser(
  User user,
) = _GotFirebaseUser;

/// if you're planning to intergrate an external backend with a custom user model
const factory AuthState.success(User firebaseUser, CustomUser user) =
    _Success;

/// when firebase SMS auto-retrieval times out
const factory AuthState.codeRetreivalTimedOut() = _CodeTimedOut;

/// when an error occurred during phone number verification.
const factory AuthState.verificationError(
  FirebaseAuthException firebaseAuthException, {
  String? verificationId,
  String? message,
}) = _VerificationError;

/// when an invalid-phone-number error occurs during phone number verification
const factory AuthState.invalidPhoneNumber(
  FirebaseAuthException firebaseAuthException,
  Function(String) retry,
  String message,
) = _InvalidPhoneNumber;
const factory AuthState.unknownError(Object error, StackTrace? stackTrace) =
    _UnknownError;
}

在我们的视图中,我们读取身份验证状态,并将各种状态映射到根据状态需要显示的 widget。没有 if 语句,只有纯粹的函数式转换。感谢 freezed

  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final authState = watch(phoneVerificationStateProvider);
    final authStateNotifier = watch(phoneVerificationStateProvider.notifier);
    return authState.when(
      initial: () => WelcomePage(
        onAcceptButtonPressed: () {
          authStateNotifier.acceptToUsePhone;
        },
      ),
      loading: (msg) => LoadingWidget(msg: msg),
      waitingForUserInput: () => PhoneInputWidget(
        verifyFunction: (validatedPhone) {
          authStateNotifier.verifyPhoneNumber(validatedPhone);
        },
      ),
      codeSent: (verificationId) => SMSCodeInputWidget(
        onTap: (smsCode) {
          authStateNotifier.verifyCode(smsCode, verificationId);
        },
      ),
      gotFirebaseUser: (user) => whenAuthentcated(user),
      success: (firebaseUser, customUser) => whenAuthentcated(firebaseUser),
      codeRetreivalTimedOut: () => CodeRetrievalTimedOutWidget(
        onTryAgain: () => authStateNotifier.retry,
      ),
      verificationError: (_, verificationId, msg) => InvalidCodeWidget(
        verificationId: verificationId,
        invalidCodeMsg: msg,
        onEnterNewCode: () {
          if (verificationId != null) {
            authStateNotifier.reEnterVerificationCode(verificationId);
          }
        },
        onUseDifferentPhone: () => authStateNotifier.retry,
      ),
      invalidPhoneNumber: (_, retryFn, _) => InvalidPhoneNumberWidget(
        onChangePhoneNumber: () {
          authStateNotifier.retry;
        },
      ),
      unknownError: (error, stackTrace) => GenericErrorWidget(
        error: error,
        message: 'Unknown Error occured',
        retryWidget: ElevatedButton(
          onPressed: () {
            authStateNotifier.retry;
          },
          child: Container(
            child: const Text('Start again'),
            alignment: Alignment.center,
            width: Adaptive.w(40),
          ),
        ),
      ),
    );
  }

使用模板的步骤

  1. 克隆存储库并重命名包为您想要调用的名称。
    flutter pub run change_app_package_name:main com.new.package.name
  2. 运行 flutter create --org com.new.package.name
  3. 按照 FlutterFire 上的说明设置 Firebase。
  4. 确保已在 Firebase 控制台中为您的项目启用了电话身份验证。
  5. 对于 Android,请在此处 启用 SafetyNet
  6. 将 google-services.json 文件下载到 android/app。
  7. Run the app.

问题

已知问题

  • 未在 iOS 上测试。
  • 未编写测试。

未知问题

如果在运行示例时遇到任何问题,请随时打开一个 issue。

GitHub

查看 Github