繁体   English   中英

如何正确使用 bloc 模式使用 Firebase 电话身份验证登录用户?

[英]How to use bloc pattern correctly to signin user using Firebase Phone Auth?

我正在使用 Firebase 电话身份验证来登录用户,并使用 flutter_bloc 来管理状态。

如果用户已经存在,将显示 HomeScreen。
否则,如果用户是新用户,注册过程将开始,用户提供他的姓名和 email。
此名称和 email 保存在 FirbaseAuth“用户”数据库以及“用户”集合下的 Firestore 中。
接下来,用户必须 select 一个城市,然后 HomeScreen 显示该城市可用的产品。

我面临的问题:-
用户在重新启动时注销。
没有正确实现 else-if 块,这会在 phoneNumber、smsCode 验证和 smsCodeAutoRetrieval 超时时导致错误状态。

我打算公开这个项目,这可以帮助其他初学者。 请查看代码并帮助我纠正集团模式并提出改进建议。 github上有一个视频演示。

GitHub回购链接

PhoneAuthBloc:-

import 'package:equatable/equatable.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:phone_auth_demo/models/phone_auth.dart';
import 'package:phone_auth_demo/repo/phone_auth_repo.dart';
part 'phone_auth_event.dart';
part 'phone_auth_state.dart';
class PhoneAuthBloc extends Bloc<PhoneAuthEvent, PhoneAuthState> {
  final PhoneAuthRepository _phoneAuthRepository;

  PhoneAuthBloc({
    required PhoneAuthRepository phoneAuthRepository,
  })  : _phoneAuthRepository = phoneAuthRepository,
        super(PhoneAuthInitial()) {
    on<PhoneAuthNumberVerified>(_phoneAuthNumberVerifiedToState);
    on<PhoneAuthCodeVerified>(_phoneAuthCodeVerifiedToState);
    on<PhoneAuthCodeAutoRetrevalTimeout>((event, emit) {
      emit(
        PhoneAuthCodeAutoRetrevalTimeoutComplete(
          event.verificationId,
        ),);
    });
    on<PhoneAuthCodeSent>((event, emit) {
      emit(
        PhoneAuthCodeSentSuccess(
          verificationId: event.verificationId,
        ),);
      emit(PhoneAuthNumberVerificationSuccess(
          verificationId: event.verificationId));
    });
    on<PhoneAuthVerificationFailed>((event, emit) {
      emit(
        PhoneAuthNumberVerficationFailure(
          event.message,
        ),);
    });
    on<PhoneAuthVerificationCompleted>((event, emit) async {
      emit(
        PhoneAuthCodeVerificationSuccess(
          uid: event.uid,
        ),);
    });
    on<PhoneAuthLoggedOut>((event, emit) async {
      emit(PhoneAuthLoading());
      await _phoneAuthRepository.unAuthenticate();
      emit(PhoneAuthInitial());
    });
  }
  void _phoneAuthNumberVerifiedToState(
      PhoneAuthNumberVerified event, Emitter<PhoneAuthState> emit) async {
    try {
      emit(PhoneAuthLoading());
      await _phoneAuthRepository.verifyPhoneNumber(
        phoneNumber: event.phoneNumber,
        onCodeAutoRetrievalTimeOut: _onCodeAutoRetrievalTimeout,
        onCodeSent: _onCodeSent,
        onVerificaitonFailed: _onVerificationFailed,
        onVerificationCompleted: _onVerificationCompleted,
      );
    } on Exception catch (e) {
      print('Exception occured while verifying phone number ${e.toString()}');
      emit(PhoneAuthNumberVerficationFailure(e.toString()));
    }
  }
 void _phoneAuthCodeVerifiedToState(
      PhoneAuthCodeVerified event, Emitter<PhoneAuthState> emit) async {
    try {
      emit(PhoneAuthLoading());
      PhoneAuthModel phoneAuthModel = await _phoneAuthRepository.verifySMSCode(
          smsCode: event.smsCode, verificationId: event.verificationId);
      emit(PhoneAuthCodeVerificationSuccess(uid: phoneAuthModel.uid));
    } on Exception catch (e) {
      print('Excpetion occured while verifying OTP code ${e.toString()}');
      emit(PhoneAuthCodeVerficationFailure(e.toString(), event.verificationId));
    }
  }
  void _onVerificationCompleted(PhoneAuthCredential credential) async {
    final PhoneAuthModel phoneAuthModel =
        await _phoneAuthRepository.verifyWithCredential(credential: credential);
 if (phoneAuthModel.phoneAuthModelState == PhoneAuthModelState.verified) {
      add(PhoneAuthVerificationCompleted(phoneAuthModel.uid));
    }
  }
 void _onVerificationFailed(FirebaseException exception) {
    print(
        'Exception has occured while verifying phone number: ${exception.toString()}');
    add(PhoneAuthVerificationFailed(exception.toString()));
  }
void _onCodeSent(String verificationId, int? token) {
    print(
        'Print code is successfully sent with verification id $verificationId and token $token');
 add(PhoneAuthCodeSent(
      token: token,
      verificationId: verificationId,
    ));
  }
 void _onCodeAutoRetrievalTimeout(String verificationId) {
    print('Auto retrieval has timed out for verification ID $verificationId');
    add(PhoneAuthCodeAutoRetrevalTimeout(verificationId));
  }
}

电话验证状态:

part of 'phone_auth_bloc.dart';
abstract class PhoneAuthState extends Equatable {
  const PhoneAuthState();
  @override
  List<Object?> get props => [];
}
class PhoneAuthInitial extends PhoneAuthState {}
class PhoneAuthStarted extends PhoneAuthState {}
class PhoneAuthLoading extends PhoneAuthState {}
class PhoneAuthError extends PhoneAuthState {}
class PhoneAuthNumberVerficationFailure extends PhoneAuthState {
  final String message;
  const PhoneAuthNumberVerficationFailure(this.message);
  @override
  List<Object> get props => [props];
}
class PhoneAuthNumberVerificationSuccess extends PhoneAuthState {
  final String verificationId;
  const PhoneAuthNumberVerificationSuccess({
    required this.verificationId,
  });
  @override
  List<Object> get props => [verificationId];
}
class PhoneAuthCodeSentSuccess extends PhoneAuthState {
  final String verificationId;
  const PhoneAuthCodeSentSuccess({
    required this.verificationId,
  });
  @override
  List<Object> get props => [verificationId];
}
class PhoneAuthCodeVerficationFailure extends PhoneAuthState {
  final String message;
  final String verificationId;
  const PhoneAuthCodeVerficationFailure(this.message, this.verificationId);
  @override
  List<Object> get props => [message];
}
class PhoneAuthCodeVerificationSuccess extends PhoneAuthState {
  final String? uid;
  const PhoneAuthCodeVerificationSuccess({
    required this.uid,
  });
  @override
  List<Object?> get props => [uid];
}
class PhoneAuthCodeAutoRetrevalTimeoutComplete extends PhoneAuthState {
  final String verificationId;
  const PhoneAuthCodeAutoRetrevalTimeoutComplete(this.verificationId);
  @override
  List<Object> get props => [verificationId];
}

电话验证事件:

part of 'phone_auth_bloc.dart';
abstract class PhoneAuthEvent extends Equatable {
  const PhoneAuthEvent();
  @override
  List<Object?> get props => [];
}
class PhoneAuthNumberVerified extends PhoneAuthEvent {
  final String phoneNumber;
  const PhoneAuthNumberVerified({
    required this.phoneNumber,
  });
  @override
  List<Object> get props => [phoneNumber];
}
class PhoneAuthCodeSent extends PhoneAuthEvent {
  final String verificationId;
  final int? token;
  const PhoneAuthCodeSent({
    required this.verificationId,
    required this.token,
  });
  @override
  List<Object> get props => [verificationId];
}
class PhoneAuthCodeVerified extends PhoneAuthEvent {
  final String verificationId;
  final String smsCode;
  const PhoneAuthCodeVerified({
    required this.verificationId,
    required this.smsCode,
  });
  @override
  List<Object> get props => [smsCode];
}
class PhoneAuthCodeAutoRetrevalTimeout extends PhoneAuthEvent {
  final String verificationId;
  const PhoneAuthCodeAutoRetrevalTimeout(this.verificationId);
  @override
  List<Object> get props => [verificationId];
}
class PhoneAuthVerificationFailed extends PhoneAuthEvent {
  final String message;
  const PhoneAuthVerificationFailed(this.message);
  @override
  List<Object> get props => [message];
}
class PhoneAuthVerificationCompleted extends PhoneAuthEvent {
  final String? uid;
  const PhoneAuthVerificationCompleted(this.uid);
  @override
  List<Object?> get props => [uid];
}
class PhoneAuthLoggedOut extends PhoneAuthEvent {}

我的应用程序:-

lass MyApp extends StatelessWidget {
  const MyApp({
    Key? key,
  }) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => PhoneAuthBloc(
        phoneAuthRepository: PhoneAuthRepository(
          phoneAuthFirebaseProvider: PhoneAuthFirebaseProvider(
            firebaseAuth: FirebaseAuth.instance,
          ),),),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const HomePage(),
      ),);}}

class HomePage extends StatelessWidget {
  const HomePage({super.key});
 @override
  Widget build(BuildContext context) {
    return BlocConsumer<PhoneAuthBloc, PhoneAuthState>(
      listener: (context, state) {
        if (state is PhoneAuthError) {
          Navigator.pushReplacement(
            context,
            MaterialPageRoute(
              builder: (_) => LoginScreen(),
            ),);}},
      builder: (context, state) {
        if (state is PhoneAuthInitial) {
          Future.delayed(Duration.zero, () {
            Navigator.pushReplacement(
              context,
              MaterialPageRoute(
                builder: (_) => LoginScreen(),
              ),);});
        } else if (state is PhoneAuthLoading) {
          return const CircularProgressIndicator();
        } else if (state is PhoneAuthCodeVerificationSuccess) {
          return const HomeScreen();
        } else {
          return const Scaffold(
            body: CircularProgressIndicator(),
          );}
        return Container();
      },);}}

PhoneAuthProvider

class PhoneAuthFirebaseProvider {
  final FirebaseAuth _firebaseAuth;

  PhoneAuthFirebaseProvider({
    required FirebaseAuth firebaseAuth,
  }) : _firebaseAuth = firebaseAuth;

  Future<void> verifyPhoneNumber({
    required String mobileNumber,
    required onVerificationCompleted,
    required onVerificaitonFailed,
    required onCodeSent,
    required onCodeAutoRetrievalTimeOut,
  }) async {
    await _firebaseAuth.verifyPhoneNumber(
      phoneNumber: mobileNumber,
      verificationCompleted: onVerificationCompleted,
      verificationFailed: onVerificaitonFailed,
      codeSent: onCodeSent,
      codeAutoRetrievalTimeout: onCodeAutoRetrievalTimeOut,
      //timeout: const Duration(seconds: 5),
    );
  }

  Future<User?> loginWithSMSVerificationCode(
      {required String verificationId,
      required String smsVerficationcode}) async {
    final AuthCredential credential = _getAuthCredentialFromVerificationCode(
        verificationId: verificationId, verificationCode: smsVerficationcode);
    return await authenticationWithCredential(credential: credential);
  }

  Future<User?> authenticationWithCredential(
      {required AuthCredential credential}) async {
    UserCredential userCredential =
        await _firebaseAuth.signInWithCredential(credential);
    if (userCredential.user != null) {
      final uid = userCredential.user!.uid;
      final userSanp =
          await FirebaseFirestore.instance.collection('users').doc(uid).get();
      if (!userSanp.exists) {
        await FirebaseFirestore.instance.collection('users').doc(uid).set({
          'uid': uid,
          'phone': userCredential.user!.phoneNumber,
          'createdAt': DateTime.now(),
          'name': '',
          'email': '',
          'city': '',
        });
      }}
    return userCredential.user;
  }
 AuthCredential _getAuthCredentialFromVerificationCode(
      {required String verificationId, required String verificationCode}) {
    return PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: verificationCode,
    );
  }
Future<void> logout() async {
    await _firebaseAuth.signOut();
  }
}

我会试着说它只是序言......我们必须创建一个事件,一旦我们启动应用程序(将从主页调用)就会触发它检查我们是否有当前用户在缓存中......通过得到

mUserId = FirebaseAuth.instance.currentUser.;uid;

然后,如果我们有用户 ID,我们会发出 state ,因为我们想要登录页面。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM