1. 依赖
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 authenticate({
@required String username,
@required String password,
}) async {
await Future.delayed(Duration(seconds: 1));
return "token";
}
/// 删除Token
Future deleteToToken() async {
await Future.delayed(Duration(seconds: 1));
return ;
}
/// 保存Token
/// @param token 令牌
Future persistToken(String token) async {
// 保存
await Future.delayed(Duration(seconds: 1));
return ;
}
/// 判断是否有Token
/// @return true: 有; false: 没有Token
Future 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
4. 认证事件
- AppStarted - App 启动事件
- LoggedIn - 登录事件
- LoggedOut - 退出登录事件
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
/// 认证事件
abstract class AuthenticationEvent extends Equatable {
const AuthenticationEvent();
@override
List
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 {
// 用户仓库
final UserRepository userRepository;
AuthenticationBloc({@required this.userRepository}): assert(userRepository != null);
@override
AuthenticationState get initialState => AuthenticationUninitialized();
@override
Stream 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(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 get props => [];
}
/// 登录初始化状态
class LoginInitial extends LoginState {}
/// 正在登录中状态
class LoginLoading extends LoginState {}
/// 登录失败状态
class LoginFailure extends LoginState {
final String error;
const LoginFailure({@required this.error});
@override
List 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 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 {
/// 用户信息仓库
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 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 createState() => _LoginFormState();
}
/// 登录状态表单
class _LoginFormState extends State {
/// 用户名控制器
final _usernameController = TextEditingController();
/// 密码控制器
final _passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
_onLoginPressed() {
BlocProvider.of(context).add(LoginPressed(
username: _usernameController.text,
password: _passwordController.text));
}
return BlocListener(
listener: (ctx, state) {
if (state is LoginFailure) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('${state.error}'),
backgroundColor: Colors.red,
));
}
},
child: BlocBuilder(
builder: (ctx, state) {
return Form(
child: Column(
children: [
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(
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(
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(),
),
);
}
}
效果图: