flutter_bloc 是基于 BLoC(Business Logic Component)模式的 Flutter 状态管理库,它封装了 bloc package,帮助我们更清晰地组织业务逻辑与 UI 的分离。核心思想是事件驱动 和 状态响应。
核心概念
- Event(事件):用户的输入或其他外部触发,比如按钮点击。
- State(状态):界面状态的表现,比如加载中、成功、失败。
- Bloc(逻辑组件):接收事件 -> 处理逻辑 -> 发出新状态。
流程图如下:
UI → Bloc.add(Event) → Bloc → emit(State) → UI rebuild
实战:以登录流程为例
1. 封装数据层
class UserService {
static Future<ApiResponse> smsLoginCode(String mobile) async {
final response = await HttpUtil().post('/api/sms/sendLoginCode',
data: { 'mobile': mobile,} );
return ApiResponse.fromJson(response, null);
}
static Future<ApiResponse<Token>> loginCode(String mobile, String passWord) async {
final response = await HttpUtil().post('/api/smsLogin',
data: { 'passWord': passWord, 'userName': mobile} );
return ApiResponse<Token>.fromJson(response, (json) => Token.fromJson(json));
}
}
2. 封装 BLoc(逻辑层)
1.定义 Event
part of 'login_bloc.dart';
abstract class LoginEvent {}
class PhoneSmsLogin extends LoginEvent {
PhoneSmsLogin({ required this.phone, required this.smsCode});
final String phone;
final String smsCode;
}
2.定义 State
part of 'login_bloc.dart';
abstract class LoginState {}
class LoginInitial extends LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {}
class LoginFailure extends LoginState {}
3.实现 BLoC
part 'login_event.dart';
part 'login_state.dart';
class LoginBloc extends Bloc<LoginEvent, LoginState> {
LoginBloc() : super(LoginInitial()) {
on<PhoneSmsLogin>((event, emit) async {
emit(LoginLoading());
ApiResponse<Token> token = await UserService.loginCode(event.phone, event.smsCode);
UserStorage.setToken(token.data!);
emit(LoginSuccess());
});
}
}
- UI 层使用 BLoC(展示层)
main.dart
runApp(
MultiBlocProvider(
providers: [
BlocProvider(create: (context) => LoginBloc()),
],
child: const App()
)
);
登录页
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
forceMaterialTransparency: true,
toolbarHeight: 0,
),
body: BlocProvider.value(
value: context.read<LoginBloc>(),
child: BlocConsumer<LoginBloc, LoginState>(
builder: (context, state) {
// if (state is LoginLoading) {
// return Center(child: Text("正在请求服务器"),);
// }
return Container(
alignment: Alignment.center,
margin: const EdgeInsets.only(top: 50.0),
child: Column(
children: [
_logo(),
_phone(),
_sms(),
_loginBtn(),
],
),
);
},
listener: (context, state) {
if (state is LoginLoading) {
} else if (state is LoginSuccess) {
GoRouterUtil.pop(context);
} else if (state is LoginFailure) {
}
}
),
),
);
}