![](/img/trans.png)
[英]Should I use bloc for the firebase authentication in flutter?
[英]Flutter Firebase Phone Authentication with BLoC
我正在尝试使用 BLoC 模式实现 Firebase 电话身份验证。
这是我的集团 class
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final AuthProvider authProvider;
AuthBloc({this.authProvider}) : assert(authProvider!= null);
@override
AuthState get initialState => Uninitialized();
@override
Stream<AuthState> mapEventToState(AuthEvent event) async* {
if (event is AppLaunched) {
yield* _mapAppLaunchedToState();
} else if(event is OtpRequested) {
yield* _mapOtpRequestedToState();
} else if (event is LoggedIn) {
yield* _mapLoggedInToState();
} else if (event is LoggedOut) {
yield* _mapLoggedOutToState();
}
}
Stream<AuthState> _mapAppLaunchedToState() async* {
try {
final isSignedIn = await authProvider.isLoggedIn();
if (isSignedIn) {
final name = userProvider.firebaseUser;
yield Authenticated(name);
} else {
yield Unauthenticated();
}
} catch (_) {
yield Unauthenticated();
}
}
Stream<AuthState> _mapOtpRequestedTostate() async* {
yield AuthInProgress();
try {
FirebaseUser firebaseUser = await authProvider.verifyPhone();
if (firebaseUser != null) {
yield Authenticated(firebaseUser);
} else {
yield Unauthenticated();
}
} catch(_, stacktrace) {
yield Unauthenticated();
}
}
Stream<AuthState> _mapLoggedInToState() async* {
yield Authenticated(userProvider.firebaseUser);
}
Stream<AuthState> _mapLoggedOutToState() async* {
yield Unauthenticated();
authProvider.signOutUser();
}
}
这是 AuthProvider
class AuthProvider extends BaseAuthProvider {
String _verificationId;
FirebaseUser user;
final FirebaseAuth _firebaseAuth;
AuthProvider(
{FirebaseAuth firebaseAuth})
: _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance;
@override
Future<FirebaseUser> verifyPhone() async {
final PhoneVerificationCompleted verificationCompleted =
(AuthCredential phoneAuthCredential) async {
user = (await _firebaseAuth.signInWithCredential(phoneAuthCredential)).user;
};
final PhoneVerificationFailed verificationFailed =
(AuthException authException) {
print(
'Phone number verification failed. Code: ${authException.code}. Message: ${authException.message}');
};
final PhoneCodeSent codeSent =
(String verificationId, [int forceResendingToken]) async {
_verificationId = verificationId;
};
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
_verificationId = verificationId;
};
await _firebaseAuth.verifyPhoneNumber(
phoneNumber: _phoneNumberProvider.number,
timeout: const Duration(seconds: 5),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
return user;
}
Future<FirebaseUser> signInWithPhone() async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: _verificationId,
smsCode: _otpProvider.number,
);
final FirebaseUser user =
(await _firebaseAuth.signInWithCredential(credential)).user;
final FirebaseUser currentUser = await _firebaseAuth.currentUser();
assert(user.uid == currentUser.uid);
if (user != null) {
return currentUser;
} else {
return null;
}
}
@override
Future<void> signOutUser() async {
return Future.wait([_firebaseAuth.signOut()]); // terminate the session
}
@override
Future<FirebaseUser> getCurrentUser() async {
return await _firebaseAuth.currentUser(); //retrieve the current user
}
@override
Future<bool> isLoggedIn() async {
final user =
await _firebaseAuth.currentUser(); //check if user is logged in or not
return user != null;
}
@override
void dispose() {}
}
当来自 AuthBloc 的 verifyPhone 被调用时,它会异步执行,然后又会调用再次异步的 mcallbacks。 所以 _mapOtpRequestedToState() 将在我们从 AuthProvider 取回 FirebaseUser 之前完成。 因此,没有产生经过身份验证的 State 并且用户没有登录。
需要帮助!!!
我认为大多数时候,可读的代码要好得多。
以下示例使用(Action -> Event)机制实现了您打算编写的逻辑:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provider<AppStateBloc>(
builder: (_) => AppStateBloc(),
dispose: (_, bloc) {
bloc.dispose();
},
child: MaterialApp(
home: TestPage(),
),
);
}
}
class TestPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
AppStateBloc appStateBloc = Provider.of<AppStateBloc>(context, listen: false);
return Scaffold(
appBar: AppBar(title: Text('Flow Test')),
body: Column(
children: <Widget>[
StreamBuilder<AppState>(
stream: appStateBloc.stateOut,
initialData: AppState.initial,
builder: (BuildContext context, AsyncSnapshot<AppState> snapshot) {
AppState state = snapshot.data;
return Column(
children: <Widget>[
Text('Current State: $state'),
SizedBox(height: 10.0),
if (state == AppState.initial || state == AppState.failure)
RaisedButton(
onPressed: () => appStateBloc.actionIn(AppStateAction.login),
child: Text('Authenticate'),
),
if (state == AppState.authenticated)
RaisedButton(
onPressed: () => appStateBloc.actionIn(AppStateAction.logout),
child: Text('Logout'),
),
],
);
},
),
],
),
);
}
}
class AppStateBloc {
StreamController<AppState> _controllerState = StreamController<AppState>.broadcast();
Stream<AppState> get stateOut => _controllerState.stream;
Function(AppState) get _stateIn => _controllerState.sink.add;
StreamController<AppStateAction> _controllerAction = StreamController<AppStateAction>.broadcast();
Function(AppStateAction) get actionIn => _controllerAction.sink.add;
StreamSubscription _subscription;
AppStateBloc() {
_subscription = _controllerAction.stream.listen(_businessLogic);
}
// All the business logic comes here
void _businessLogic(AppStateAction action) async {
switch (action) {
case AppStateAction.login:
// do authentication
User user = await fakeAuthenticator.verifyUser();
if (user == null) {
_stateIn(AppState.failure);
} else {
_stateIn(AppState.authenticated);
}
break;
case AppStateAction.logout:
// do what needs to be done in this case
await fakeAuthenticator.logout();
_stateIn(AppState.initial);
break;
default:
// nothing
break;
}
}
void dispose() {
_subscription?.cancel();
_controllerAction?.close();
_controllerState?.close();
}
}
enum AppStateAction {
none,
login,
logout,
}
enum AppState {
initial,
authenticated,
failure,
}
class User {}
class FakeAuthenticator {
User _user;
Future<User> verifyUser() async {
// Simulation of Authentication made at server side
await Future.delayed(const Duration(seconds: 1));
// Successful authentication
_user = User();
return _user;
}
Future<void> logout() async {
// Simulation of Authentication made at server side
await Future.delayed(const Duration(seconds: 1));
_user = null;
}
User get user => _user;
// ------- Singleton
static final FakeAuthenticator _instance = FakeAuthenticator._internal();
factory FakeAuthenticator() => _instance;
FakeAuthenticator._internal();
}
FakeAuthenticator fakeAuthenticator = FakeAuthenticator();
与您的代码的主要区别在于,与此代码不同,但这是我个人的感觉,您更能“控制”您的业务逻辑。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.