The logic to continuously monitor the user’s auth status. This snippet is using Firebase for user authentication and the bloc package.
Dependencies
Add the following packages in the pubspec.yaml first.
- cloud_firestore: ^version
- firebase_auth: ^version
- flutter_bloc: ^version
- equatable: ^version
and run the pub get command from the terminal.
auth_repo.dart
class AuthenticationRepository {
/// {@macro authentication_repository}
AuthenticationRepository({firebase_auth.FirebaseAuth firebaseAuth})
: _firebaseAuth = firebaseAuth ?? firebase_auth.FirebaseAuth.instance;
final firebase_auth.FirebaseAuth _firebaseAuth;
/// Stream of [FirebaseUser] which will emit the current user when
/// the authentication state changes.
///
/// Emits [FirebaseUser.empty] if the user is not authenticated.
Stream<User> get user {
return _firebaseAuth.authStateChanges().map((firebaseUser) {
return firebaseUser == null ? User.empty : firebaseUser.toUser;
});
}
...
}
auth_bloc.dart
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
AuthenticationBloc()
: _authenticationRepository = AuthenticationRepository(),
super(const AuthenticationState.unknown()) {
_userSubscription = _authenticationRepository.user.listen(
(user) => add(AuthenticationUserChanged(user)),
);
}
final AuthenticationRepository _authenticationRepository;
StreamSubscription<User> _userSubscription;
@override
Stream<AuthenticationState> mapEventToState(
AuthenticationEvent event,
) async* {
if (event is AuthenticationUserChanged) {
yield _mapAuthenticationUserChangedToState(event);
} else if (event is AuthenticationLogoutRequested) {
unawaited(_authenticationRepository.logOut());
}
}
@override
Future<void> close() {
_userSubscription?.cancel();
return super.close();
}
AuthenticationState _mapAuthenticationUserChangedToState(
AuthenticationUserChanged event,
) {
return event.user != User.empty
? AuthenticationState.authenticated(event.user)
: const AuthenticationState.unauthenticated();
}
}
auth_event.dart
abstract class AuthenticationEvent extends Equatable {
const AuthenticationEvent();
@override
List<Object> get props => [];
}
class AuthenticationUserChanged extends AuthenticationEvent {
const AuthenticationUserChanged(this.user);
final User user;
@override
List<Object> get props => [user];
}
class AuthenticationLogoutRequested extends AuthenticationEvent {}
auth_state.dart
enum AuthenticationStatus { authenticated, unauthenticated, unknown }
class AuthenticationState extends Equatable {
const AuthenticationState._({
this.status = AuthenticationStatus.unknown,
this.user = User.empty, /// Empty user which represents an unauthenticated user.
});
const AuthenticationState.unknown() : this._();
const AuthenticationState.authenticated(User user)
: this._(status: AuthenticationStatus.authenticated, user: user);
const AuthenticationState.unauthenticated()
: this._(status: AuthenticationStatus.unauthenticated);
final AuthenticationStatus status;
final User user;
@override
List<Object> get props => [status, user];
}
Authentication repository and bloc initialization
Future<void> main() async {
await init();
runApp(
MyApp(),
);
}
void init() async {
HydratedBloc.storage = await HydratedStorage.build();
await Firebase.initializeApp();
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RepositoryProvider.value(
// The Repository is the parent class.
value: Repository(
MockRepository(), RemoteRepository(), AuthenticationRepository()),
child: _buildMainChild(context));
}
_buildMainChild(BuildContext context) {
return MultiBlocProvider(
providers: [
...
BlocProvider<AuthenticationBloc>(
create: (context) => AuthenticationBloc()),
BlocProvider<LoginBloc>(create: (context) => LoginBloc())
],
child: MaterialApp(home: _buildRoot),
);
}
_buildRoot() {
return BlocBuilder<AuthenticationBloc, AuthenticationState>(
cubit: BlocProvider.of<AuthenticationBloc>(context),
builder: (context, state) { ... ... };
}
}
/// in each place we need to listen for that change (or as a direct child of the builder child)
/// we provide at the main widget, we listen at the rest.
class ProfileScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiBlocListener(
listeners: [
BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {},
)
],
child: child: ProfilePage();
}
}