Flutter状态管理Bloc之登录
mazaiting 人气:01. 依赖
dependencies: flutter_bloc: ^2.1.1 equatable: ^1.0.1
2. UserRepository 用于管理用户数据
提供认证方法,删除Token,保存Token,是否包含Token四个方法。
import 'package:flutter/material.dart'; /// 用户数据仓库 class UserRepository { /// 认证 /// @param username 用户名 /// @param password 密码 /// @return 返回认证信息 Future<String> authenticate({ @required String username, @required String password, }) async { await Future.delayed(Duration(seconds: 1)); return "token"; } /// 删除Token Future<void> deleteToToken() async { await Future.delayed(Duration(seconds: 1)); return ; } /// 保存Token /// @param token 令牌 Future<void> persistToken(String token) async { // 保存 await Future.delayed(Duration(seconds: 1)); return ; } /// 判断是否有Token /// @return true: 有; false: 没有Token Future<bool> hasToken() async { // 读取Token await Future.delayed(Duration(seconds: 1)); return false; } }
3. AuthenticateState
- uninitialized - 身份验证未初始化
- loading - 等待保存/删除Token
- authenticated - 认证成功
- unauthenticated - 未认证
import 'package:equatable/equatable.dart'; /// 认证状态 abstract class AuthenticationState extends Equatable { @override List<Object> get props => []; } /// - uninitialized - 身份验证未初始化 class AuthenticationUninitialized extends AuthenticationState {} /// - loading - 等待保存/删除Token class AuthenticationLoading extends AuthenticationState {} /// - authenticated - 认证成功 class AuthenticationAuthenticated extends AuthenticationState {} /// - unauthenticated - 未认证 class AuthenticationUnauthenticated extends AuthenticationState {}
4. 认证事件
- AppStarted - App 启动事件
- LoggedIn - 登录事件
- LoggedOut - 退出登录事件
import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; /// 认证事件 abstract class AuthenticationEvent extends Equatable { const AuthenticationEvent(); @override List<Object> get props => []; } /// APP 启动事件 class AppStart extends AuthenticationEvent {} /// APP 登录事件 class LoginIn extends AuthenticationEvent { final String token; const LoginIn({@required this.token}); @override List<Object> get props => [token]; @override String toString() => "LoggedIn { token: $token }"; } /// APP 退出登录事件 class LoginOut extends AuthenticationEvent {}
5. AuthenticationBloc
- 实现构造方法
- 实现initialState方法
- 实现mapEventToState方法
import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart'; import './bloc.dart'; import 'package:state_manage/login/user_repository.dart'; /// 认证Bloc class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> { // 用户仓库 final UserRepository userRepository; AuthenticationBloc({@required this.userRepository}): assert(userRepository != null); @override AuthenticationState get initialState => AuthenticationUninitialized(); @override Stream<AuthenticationState> mapEventToState(AuthenticationEvent event) async* { if (event is AppStarted) { // 判断是否有Token final bool hasToken = await userRepository.hasToken(); if (hasToken) { yield AuthenticationAuthenticated(); } else { yield AuthenticationUnauthenticated(); } } else if (event is LoggedIn) { yield AuthenticationLoading(); await userRepository.persistToken(event.token); yield AuthenticationAuthenticated(); } else if (event is LoggedOut) { yield AuthenticationLoading(); await userRepository.deleteToToken(); yield AuthenticationUnauthenticated(); } } }
6. SplashPage 启动页
import 'package:flutter/material.dart'; /// 启动页 class SplashPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Text('Splash Screen'), ), ); } }
7. HomePage 主页
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:state_manage/login/bloc/bloc.dart'; /// 主页 class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Home'), ), body: Container( child: Center( child: RaisedButton( child: Text('logout'), onPressed: () => BlocProvider.of<AuthenticationBloc>(context).add(LoggedOut()) ), ), ), ); } }
8. LoginState 登录状态
- LoginInitial - 初始化状态
- LoginLoading - 登录中状态
- LoginFailure - 登录失败状态
import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; /// 登录状态 @immutable abstract class LoginState extends Equatable { const LoginState(); @override List<Object> get props => []; } /// 登录初始化状态 class LoginInitial extends LoginState {} /// 正在登录中状态 class LoginLoading extends LoginState {} /// 登录失败状态 class LoginFailure extends LoginState { final String error; const LoginFailure({@required this.error}); @override List<Object> get props => [error]; @override String toString() => "LoginFailure { error: $error }"; }
9. LoginEvent 登录事件
import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; /// 登录事件 @immutable abstract class LoginEvent extends Equatable{} /// 登录事件 class LoginPressed extends LoginEvent { /// 用户名 final String username; /// 密码 final String password; LoginPressed({ @required this.username, @required this.password }); @override List<Object> get props => [username, password]; @override String toString() => "LoginPressed { username: $username, password: $password }"; }
10. LoginBloc 实现
import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:state_manage/login/user_repository.dart'; import './bloc.dart'; class LoginBloc extends Bloc<LoginEvent, LoginState> { /// 用户信息仓库 final UserRepository userRepository; /// 认证Bloc final AuthenticationBloc authenticationBloc; LoginBloc({@required this.userRepository, @required this.authenticationBloc}) : assert(userRepository != null), assert(authenticationBloc != null); @override LoginState get initialState => LoginInitial(); @override Stream<LoginState> mapEventToState( LoginEvent event, ) async* { if (event is LoginPressed) { yield LoginLoading(); try { final token = await userRepository.authenticate( username: event.username, password: event.password ); authenticationBloc.add(LoggedIn(token: token)); yield LoginInitial(); } catch (error) { yield LoginFailure(error: error); } } } }
11. 登录页面
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:state_manage/login/bloc/bloc.dart'; import 'package:state_manage/login/user_repository.dart'; /// 登录页面 class LoginPage extends StatelessWidget { /// 用户信息仓库 final UserRepository userRepository; LoginPage({Key key, @required this.userRepository}) : assert(userRepository != null), super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Login'), ), body: BlocProvider( create: (ctx) => LoginBloc( authenticationBloc: BlocProvider.of(context), userRepository: userRepository), child: LoginForm(), ), ); } } /// 登录表单 class LoginForm extends StatefulWidget { @override State<StatefulWidget> createState() => _LoginFormState(); } /// 登录状态表单 class _LoginFormState extends State<LoginForm> { /// 用户名控制器 final _usernameController = TextEditingController(); /// 密码控制器 final _passwordController = TextEditingController(); @override Widget build(BuildContext context) { _onLoginPressed() { BlocProvider.of<LoginBloc>(context).add(LoginPressed( username: _usernameController.text, password: _passwordController.text)); } return BlocListener<LoginBloc, LoginState>( listener: (ctx, state) { if (state is LoginFailure) { Scaffold.of(context).showSnackBar(SnackBar( content: Text('${state.error}'), backgroundColor: Colors.red, )); } }, child: BlocBuilder<LoginBloc, LoginState>( builder: (ctx, state) { return Form( child: Column( children: <Widget>[ TextFormField( decoration: InputDecoration(labelText: 'username'), controller: _usernameController, ), TextFormField( decoration: InputDecoration(labelText: 'password'), controller: _passwordController, ), RaisedButton( onPressed: state is LoginLoading ? _onLoginPressed : null, child: Text('Login'), ), Container( child: state is LoginLoading ? CircularProgressIndicator() : null, ) ], ), ); }, ), ); } }
12. 测试页面
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:state_manage/login/bloc/bloc.dart'; import 'package:state_manage/login/home_page.dart'; import 'package:state_manage/login/login_page.dart'; import 'package:state_manage/login/splash_page.dart'; import 'package:state_manage/login/user_repository.dart'; /// 登录测试页面 class LoginTest extends StatelessWidget { // 数据仓库 final userRepository = UserRepository(); @override Widget build(BuildContext context) { return BlocProvider<AuthenticationBloc>( create: (context) => AuthenticationBloc(userRepository: userRepository)..add(AppStarted()), child: App(userRepository: userRepository), ); } } /// 应用页 class App extends StatelessWidget{ // 数据仓库 final UserRepository userRepository; App({Key key, @required this.userRepository}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: BlocBuilder<AuthenticationBloc, AuthenticationState>( builder: (context, state) { if (state is AuthenticationAuthenticated) { return HomePage(); } else if (state is AuthenticationUnauthenticated) { return LoginPage(userRepository: userRepository); } else if (state is AuthenticationLoading) { return LoadingIndicator(); } else { if (state is AuthenticationUninitialized) { return SplashPage(); } else { return SplashPage(); } } }, ), ); } } /// 加载状态 class LoadingIndicator extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, body: Center( child: CircularProgressIndicator(), ), ); } }
效果图:
加载全部内容