当前位置:网站首页>不到40行代码手撸一个BlocProvider
不到40行代码手撸一个BlocProvider
2022-06-26 12:33:00 【岛上码农@公众号同名】
前言
上一篇我们对 BLoC做了整体的介绍,了解了 BLoC 的使用后,心里不禁有点痒痒,写了好几个状态管理插件了,能不能利用 BLoC自己也撸一个 Flutter 状态管理组件,用来构建基于 BLoC 的响应式Flutter页面。说干就干,我们给这个状态管理组件命名为 SimpleBlocProvider。
SimpleBlocProvider定义
对于这个简单的状态管理组件 SimpleBlocProvider,因为需要放置到组件树中,因此肯定是一个 Widget,由于内部还需要维护数据,因此我们使用 StatefulWidget。等等!不是不推荐使用 StatefulWidget 吗?需要注意,如果这个组件自己维护自身状态,不影响外部组件那是没问题的。比如说,一个按钮点击后会有点击效果,这个其实也需要使用 StatefulWidget 实现,但是这个行为只会引起自身刷新,那是没问题的。
其次,我们要把原先 UI 组件的构建放到SimpleBlocProvider中来,那就需要定义一个 构建组件的builder 参数,所有原先 UI组件的构建都由这个 builder 来完成,同时这个 builder 应该携带最新的状态数据,以便更新 UI 组件。而且,状态数据是确定不了类型的,因此这个 builder 应该是返回 Widget 的泛型函数,定义如下。
typedef StateBuilder<T> = Widget Function(T state);
比如显示计数器的 Text,我们可以这么写:
SimpleBlocProvider<int> (
builder: (count) => Text('$count'),
)
光有 builder 还不够,我们需要 Bloc 逻辑组件,以便从逻辑组件里获取最新的状态数据,因此需要将 Bloc 逻辑组件也作为参数给 SimpleBlocProvider。于是我们就得到了SimpleBlocProvider的基本定义了。
class SimpleBlocProvider<T> extends StatefulWidget {
final StateBuilder<T> builder;
final BlocBase<T> bloc;
const SimpleBlocProvider(
{Key? key, required this.builder, required this.bloc})
: super(key: key);
@override
_SimpleBlocProviderState<T> createState() => _SimpleBlocProviderState<T>();
}
BLoC 刷新
要实现 BLoC 刷新,我们需要监听 BLoC 状态数据的变化,从上一篇我们知道BLoC 是基于 Stream 实现的,对于 Stream,可以使用其 listen 方法来监听 Stream 流数据的变化。listen 方法定义如下:
StreamSubscription<T> listen(void onData(T event)?,
{Function? onError, void onDone()?, bool? cancelOnError});
因此,我们可以在 listen 的 onData 中调用 setState 就可以做到刷新界面了。我们组件销毁的时候需要取消监听,因此我们在_SimpleBlocProviderState 中定义一个属性_streamSubscription存储 listen 方法的返回值,并在 dispose 中取消监听。
_streamSubscription = widget.bloc.stream.listen((data) {
setState(() {
_state = data;
});
});
//
@override
void dispose() {
_streamSubscription.cancel();
super.dispose();
}
接下来就比较简单了,在_SimpleBlocProviderState的 build 方法中直接返回 builder 携带状态数据_state构建组件即可。
@override
Widget build(BuildContext context) {
return widget.builder(_state);
}
这样,只要 BLoC 的状态数据发生了改变,就会通过 listen监听更新SimpleBlocProvider的_state,并刷新SimpleBlocProvider组件,从而更新了 builder 构建的组件。完整代码如下:
typedef StateBuilder<T> = Widget Function(T state);
class SimpleBlocProvider<T> extends StatefulWidget {
final StateBuilder<T> builder;
final BlocBase<T> bloc;
const SimpleBlocProvider(
{Key? key, required this.builder, required this.bloc})
: super(key: key);
@override
_SimpleBlocProviderState<T> createState() => _SimpleBlocProviderState<T>();
}
class _SimpleBlocProviderState<T> extends State<SimpleBlocProvider<T>> {
late T _state;
late StreamSubscription<T> _streamSubscription;
@override
void initState() {
_state = widget.bloc.state;
super.initState();
_streamSubscription = widget.bloc.stream.listen((data) {
setState(() {
_state = data;
});
});
}
@override
Widget build(BuildContext context) {
return widget.builder(_state);
}
@override
void dispose() {
_streamSubscription.cancel();
super.dispose();
}
}
总共不到40行代码就搞定了!
SimpleBlocProvider 应用
现在来看怎么用,我们先来一个计数器看看。
class CounterCubit extends Cubit<int> {
CounterCubit({initial = 0}) : super(initial);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
@override
void onChange(Change<int> change) {
super.onChange(change);
}
}
class SimpleBlocCounterPage extends StatelessWidget {
final counter = CounterCubit();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bloc 计数器'),
),
body: Center(
child: SimpleBlocProvider<int>(
builder: (count) => Text(
'$count',
style: TextStyle(
fontSize: 32,
color: Colors.blue,
),
),
bloc: counter,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
counter.increment();
},
tooltip: '点击增加',
child: Icon(Icons.add),
),
);
}
}

是不是和我们之前在使用 MobX,GetX 的 GetBuilder 很类似?再来看自定义类,来个简单的 Person 类,然后用 Bloc 的 event 模式试试。
class Person {
final String name;
final String gender;
const Person({required this.name, required this.gender});
}
abstract class PersonEvent {}
class UsingCnNameEvent extends PersonEvent {}
class UsingEnNameEvent extends PersonEvent {}
class PersonBloc extends Bloc<PersonEvent, Person> {
PersonBloc(Person person) : super(person) {
on<UsingCnNameEvent>(
(event, emit) => emit(Person(name: '岛上码农', gender: '男')));
on<UsingEnNameEvent>(
(event, emit) => emit(Person(name: 'island-coder', gender: 'male')));
}
}
class SimpleBlocCounterPage extends StatelessWidget {
final personBloc = PersonBloc(Person(name: '岛上码农', gender: '男'));
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bloc 事件'),
),
body: Center(
child: SimpleBlocProvider<Person>(
builder: (person) => Text(
'姓名:${person.name},性别:${person.gender}',
style: TextStyle(
fontSize: 22,
color: Colors.blue,
),
),
bloc: personBloc,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
personBloc.add(UsingEnNameEvent());
},
tooltip: '点击增加',
child: Icon(Icons.add),
),
);
}
}
运行起来也是没问题的。
总结
本篇介绍了使用 BLoC 实现简单状态管理的 SimpleBLocProvider,这个自定义的 BlocProvider不到40行,当然这个代码距离实际使用还有差距,但是对于了解一下这些第三方状态管理插件的实现机制还是有帮助的。接下来我们将使用官方的 flutter_bloc 插件来讲具体的应用实例。 
边栏推荐
- 7-16 货币系统Ⅰ
- 7-2 大盗阿福
- Scala-day03- operators and loop control
- Five trends of member management in 2022
- PHP uses laravel pay component to quickly access wechat jsapi payment (wechat official account payment)
- Introduction to the four major FPGA manufacturers abroad
- Lodash common methods (filtering, anti shake...)
- Member system + enterprise wechat + applet to help the efficient transformation of private domain
- Common problems and Thoughts on member operation management
- Laravel+gatewayworker completes the im instant messaging and file transfer functions (Chapter 4: server debugging errors)
猜你喜欢

Laravel subdomain accesses different routing files and different modules

PHP laravel+gatewayworker completes im instant messaging and file transfer (Chapter 1: basic configuration)

环形队列php

Spark-day03-core programming RDD operator

nvm安装教程

Fengshentai old shooting range Kali series

Introduction to the four major FPGA manufacturers abroad

PHP uses laravel pay component to quickly access wechat jsapi payment (wechat official account payment)

Scala-day02- variables and data types

Scala-day03- operators and loop control
随机推荐
老司机总结的12条 SQL 优化方案(非常实用)
Learning directory
Is it safe to open a securities account
Analysis report on dynamic research and investment planning suggestions of China's laser medical market in 2022
2022 edition of Beijing 5g industry investment planning and development prospect forecast analysis report
2、 MySQL Foundation
详细实操分享,下班刷了两小时的搞笑视频,一个月收益7000多
The transformation of enterprise customers' digital assets needs to suit the case
What are the top ten securities companies? Is it safe to open a mobile account?
Is it safe to open a securities account in general
How long ago did PHP get
Current situation investigation and investment prospect forecast analysis report of China's electrolytic copper market from 2022 to 2028
New routing file in laravel framework
7-16 货币系统Ⅰ
7-2 大盗阿福
Why is password salt called "salt"? [Close] - why is a password salt called a "salt"? [closed]
11、 Box styles and user interface
"Pinduoduo and short video speed version", how can I roast!
Refined operation, extending the full life cycle value LTV
nvm安装教程