状态管理:bloc、状态管理
bloc是一种mvvm基于事件状态驱动的
入门Bloc,案例代码
视频资料
视频尽量选择新一些的,bloc版本迭代到8.0.1了,里面的一些方法也有些改变,早版本的教学视频不合适
bloc插件,管理项目
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
bloc: ^8.0.3
flutter_bloc: ^8.0.1
equatable: ^2.0.3
实现的功能及界面
这里有四个按钮,当点击第一个,生成一个pizza(种类A),第二个按钮,删除一个pizza(种类A);后面两个功能类似,但是生成的pizza种类不同。
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
class Pizza extends Equatable {
final String id;
final String name;
final Image image;
const Pizza(this.id, this.name, this.image);
@override
List<Object?> get props => [id, name, image];
static List<Pizza> pizzas = [
Pizza('0', 'Pizza0', Image.asset("images/pizza0.jpg")),
Pizza('1', 'Pizza1', Image.asset("images/pizza1.jpg")),
];
}
这里的Equatable是用于比较对象的,Equatable可以为你覆写==和hashCode
只需要写这一句
// Equatable
@override
List<Object?> get props => [id, name, image];
创建之后,先配置pizza_state,这个表示在初始化、添加、删除这几个操作中,会有几个状态。
在一开始会有初始化一个抽象类,继承Equatable
part of 'pizza_bloc.dart';
abstract class PizzaState extends Equatable {
const PizzaState();
@override
List<Object> get props => [];
}
抽象类不能实例化(当然有factory可以),在这种场景下,有初始化状态,有加载pizza状态,初始化状态不需要添加什么内容,而加载状态需要构造器,用List保存现有的Pizza。
接着配置pizza_event,同样分析得出有三个事件,分别是初始化Pizza,添加Pizza,删除Pizza
part of 'pizza_bloc.dart';
abstract class PizzaEvent extends Equatable {
const PizzaEvent();
@override
List<Object> get props => [];
}
class LoadPizzaCounter extends PizzaEvent {}
class AddPizza extends PizzaEvent {
final Pizza pizza;
AddPizza({required this.pizza});
@override
List<Object> get props => [pizza];
}
class RemovePizza extends PizzaEvent {
final Pizza pizza;
RemovePizza({required this.pizza});
@override
List<Object> get props => [pizza];
}
最后是bloc的配置
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import '../models/pizza_model.dart';
part 'pizza_event.dart';
part 'pizza_state.dart';
class PizzaBloc extends Bloc<PizzaEvent, PizzaState> {
/**
* super(PizzaInitial())是将PizzaInitial()返回的结果传递给父类Bloc的构造器
*/
PizzaBloc() : super(PizzaInitial()) {
/**
* void on(
EventHandler handler, {
EventTransformer? transformer,
})
*/
on<LoadPizzaCounter>(
(event, emit) async {
//等待一秒
await Future<void>.delayed(Duration(seconds: 1));
//生成加载Pizza的状态
emit(PizzaLoaded(pizzas: <Pizza>[]));
},
);
on<AddPizza>(
(event, emit) {
if (state is PizzaLoaded) {
final state = this.state as PizzaLoaded;
emit(PizzaLoaded(
pizzas: List.from(state.pizzas)..add(event.pizza),
));
}
},
);
/**
* 如果当前状态是PizzaLoaded的状态,
* 那么将emit引发一个新的状态PizzaLoaded,
* 里面的内容将发生变化,即移除一个pizza
*/
on<RemovePizza>(
(event, emit) {
if (state is PizzaLoaded) {
final state = this.state as PizzaLoaded;
emit(PizzaLoaded(
pizzas: List.from(state.pizzas)..remove(event.pizza),
));
}
},
);
}
}
这里的bloc配置是将事件和状态关联起来,处理不同事件的逻辑操作
最后在main.dart写界面,并使用bloc
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => PizzaBloc()..add(LoadPizzaCounter()),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'The Pizza Bloc',
home: HomeScreen(),
));
}
}
这里的MultiBlocProvider的写法很像MultiProvider的注册方式,比如下面代码
main() {
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (ctx) => HYBaseDataViewModel()),
ChangeNotifierProxyProvider<HYBaseDataViewModel, HYVideoViewModel>(
create: (cts) => HYVideoViewModel(),
update: (ctx, baseDataVM, videoVM) {
videoVM?.updateBaseData(baseDataVM);
return videoVM as HYVideoViewModel;
})
],
child: MyApp(),
));
}
那么可以理解为初始化bloc,注册BlocProvider,接着创建BlocBuilder,
body: Center(
//2、创建BlocBuilder
child: BlocBuilder<PizzaBloc, PizzaState>(
builder: (context, state) {
//如果状态是初始化状态,就返回圆形加载的进度条
if (state is PizzaInitial) {
return CircularProgressIndicator(
color: Colors.orange,
);
}
//如果状态是加载完成的
if (state is PizzaLoaded) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${state.pizzas.length}', //pizza的数量
style: TextStyle(fontSize: 60, fontWeight: FontWeight.bold),
),
SizedBox(
height: 20,
),
SizedBox(
height: MediaQuery.of(context).size.height / 1.5,
width: MediaQuery.of(context).size.width,
child: Stack(
alignment: Alignment.center,
clipBehavior: Clip.none, //不裁剪
children: buildContent(state),
),
)
],
);
} else {
return const Text('Something went wrong!');
}
},
),
),
然后在FloatActionButton中编写点击事件
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () {
/**
* T read() {
return Provider.of(this, listen: false);
}
查询Bloc,,并调用添加Pizza事件来添加一个Pizza
*/
context.read<PizzaBloc>().add(AddPizza(pizza: Pizza.pizzas[0]));
},
child: Icon(Icons.local_pizza_outlined),
backgroundColor: Colors.orange[800],
),
SizedBox(
height: 10,
),
FloatingActionButton(
onPressed: () {
context.read<PizzaBloc>().add(RemovePizza(pizza: Pizza.pizzas[0]));
},
child: Icon(Icons.remove),
backgroundColor: Colors.orange[800],
),
SizedBox(
height: 10,
),
FloatingActionButton(
onPressed: () {
context.read<PizzaBloc>().add(AddPizza(pizza: Pizza.pizzas[1]));
},
child: Icon(Icons.local_pizza_rounded),
backgroundColor: Colors.orange[800],
),
SizedBox(
height: 10,
),
FloatingActionButton(
onPressed: () {
context.read<PizzaBloc>().add(RemovePizza(pizza: Pizza.pizzas[1]));
},
child: Icon(Icons.remove),
backgroundColor: Colors.orange[800],
),
SizedBox(
height: 10,
),
],
),