[英]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上有一个视频演示。
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.