欢迎前往本人的GitHub查看更多内容。点击前往GitHub
1 对于状态管理的理解
最近项目开发完成后,没有什么任务安排,就自己对项目进行了查缺补漏。如果说flutter开发中最不可避免的话,肯定是状态管理啦。我们自己的项目当中用的是Provider来进行管理的,自己其实对于状态管理的基础还是有一点模糊,粗浅的理解就是:避免对页面不停的build,要进行针对性的刷新,(列入:更新一个text也将整个页面build一次浪费性能。)
1.0 简单理解
那我们来言归正传说一说比较正确的理解吧,避免我在这里误人子弟。
1.状态管理的目的就是为了让界面与业务分离。
2.当我们的应用功能复杂多样的时候,应用程序将会有几十个甚至上百个状态,这时候我们需要对状态进行合理有效的管理。
很多从命令式编程框架(Android或iOS原生开发者)转成声明式编程(Flutter、Vue、React等)刚开始并不适应,因为需要一个新的角度来考虑APP的开发模式。Flutter作为一个现代的框架,是声明式编程的:
在编写一个应用的过程中,我们有大量的状态需要来进行管理,而正是对这些State的改变,来更新界面的刷新:
1.1 InheritedWidget(共享应用程序状态)
Flutter 中Widget 多种多样,有UI的,当然也有功能型的组件InheritedWidget 组件就是Flutter 中的一个功能组件,它可以实现Flutter 组件之间的数据共享,他的数据传递方向在Widget树传递是从上到下的。
使用样列:
class StateManagementDemo extends StatefulWidget {
@override
_StateManagementDemoState createState() => _StateManagementDemoState();
}
class _StateManagementDemoState extends State {
int _count = 0;
void _increaseCount () {
setState(() {
_count += 1;
});
}
@override
Widget build(BuildContext context) {
return CounterProvider(
count: _count,
increaseCount: _increaseCount,
child: Scaffold(
appBar: AppBar(title: Text('StateManagementDemo'), elevation: 0.0,),
body: CounterWrapper(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _increaseCount,
),
),
);
}
}
class CounterWrapper extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Counter(),);
}
}
class Counter extends StatelessWidget {
@override
Widget build(BuildContext context) {
final int count = CounterProvider.of(context).count;
final VoidCallback increaseCount = CounterProvider.of(context).increaseCount;
return Center(child: ActionChip(
label: Text('$count'),
onPressed: increaseCount,),
);
}
}
class CounterProvider extends InheritedWidget {
final int count;
final VoidCallback increaseCount;
final Widget child;
CounterProvider({
this.count,
this.increaseCount,
this.child,
}) : super(child: child);
// 其他部件中可以用这个方法得到小部件内的数据
static CounterProvider of(BuildContext context) => context.inheritFromWidgetOfExactType(CounterProvider);
// 决定是否通知继承这个小部件的小部件,当这个部件重建以后有时候需要通知继承这个部件的小部件
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
}
2 状态管理框架
2.0 可以通过如下方式来进行状态管理
1.setState :
最重要的方式 setState,支持规模较小的程序足够了,所有其它方式最终都需要调用 setState。
2.Function callback
Dart Function 足够灵活,还支持模版参数。
typedef FooChanged = void Function(int);
typedef ValueChanged = void Function(T value);
单向变更通知,可以和ObserverList结合支持多个订阅者。
Flutter 内置 ChangeNotifier, ValueNotifier 都可以认为是类似方案。
3.Delegate
可以认为是多个回调函数,其他语言里都有类似模式,名称似乎来源于 Objective-C。实际例子
abstract class SpiderDelegate {
/// category is null, crawl book whole site
/// category not null, crawl book under the category
void onBook(Book book, {Site site, Category category});
void onChapter(Book book, Chapter chapter);
}
4.Sigslot
源自 Qt 里的经典编程模式,Dart 可以轻易实现。这种方式在 Flutter 里可能根本不会有太多应用,但是由于 Sigslot 在 C++ 领域具有举足轻重的地位,属于界面数据和逻辑解耦合的王者,boost::signal(2)就是明证,在这里列出纯属凑数(本人也实现了一个)。
typedef ValueCallback = void Function(E value);
abstract class Signable {
// Signable someValue;
/// Register a closure to be called when the object notifies its listeners.
void connect(ValueCallback listener);
/// Remove a previously registered closure from the list of closures that the
/// object notifies.
void disconnect(ValueCallback listener);
/// sink value changed
void emit(E value);
}
使用方法类似
Signal signalString;
signalString.connect((String str) {
// got the `str` changed here
});
2.1 scoped_model
Scoped_model是一个dart第三方库,提供了让您能够轻松地将数据模型从父Widget传递到它的后代的功能。此外,它还会在模型更新时重新渲染使用该模型的所有子项。
它直接来自于Google正在开发的新系统Fuchsia核心Widgets 中对Model类的简单提取,作为独立使用的独立Flutter插件发布。
Scoped model使用了观察者模式,将数据模型放在父代,后代通过找到父代的model进行数据渲染,最后数据改变时将数据传回,父代再通知所有用到了该model的子代去更新状态。
2.2 Redux
Redux是一种单向数据流架构,可以轻松开发,维护和测试应用程序。
我们在Redux中,所有的状态都储存在Store里。这个Store会放在App顶层。
View拿到Store储存的状态(State)并把它映射成视图。View还会与用户进行交互,用户点击按钮滑动屏幕等等,这时会因为交互需要数据发生改变。
Redux让我们不能让View直接操作数据,而是通过发起一个action来告诉Reducer,状态得改变啦。
这时候Reducer接收到了这个action,他就回去遍历action表,然后找到那个匹配的action,根据action生成新的状态并把新的状态放到Store中。
Store丢弃了老的状态对象,储存了新的状态对象后,就通知所有使用到了这个状态的View更新(类似setState)。这样我们就能够同步不同view中的状态了。
2.3 BLoC
BLoC是一种利用reactive programming方式构建应用的方法,这是一个由流构成的完全异步的世界。
* 用StreamBuilder包裹有状态的部件,streambuilder将会监听一个流
* 这个流来自于BLoC
* 有状态小部件中的数据来自于监听的流。
* 用户交互手势被检测到,产生了事件。例如按了一下按钮。
* 调用bloc的功能来处理这个事件
* 在bloc中处理完毕后将会吧最新的数据add进流的sink中
* StreamBuilder监听到新的数据,产生一个新的snapshot,并重新调用build方法
* Widget被重新构建
2.4 RxDart
RxDart是基于ReactiveX标准API的Dart版本实现,由Dart标准库中Stream扩展而成。因此,RxDart与Dart的相关术语稍有区别:
Dart RxDart
StreamController Subject
Stream Observable
Observable等同于Stream,Subject等同于StreamController,前者均由后者继承而来。
不同于Dart,RxDart提供了三种StreamController的变体来应用到不同的场景:
PublishSubject
BehaviorSubject
ReplaySubject
2.5 Fish-Redux【推荐】
Fish Redux 是一个基于 Redux 数据管理的组装式 flutter 应用框架, 它特别适用于构建中大型的复杂应用。它的特点是配置式组装。
一方面我们将一个大的页面,对视图和数据层层拆解为互相独立的 Component|Adapter,上层负责组装,下层负责实现;
另一方面将 Component|Adapter 拆分为 View,Reducer,Effect 等相互独立的上下文无关函数。
所以它会非常干净,易维护,易协作。
Fish Redux 的灵感主要来自于 Redux, Elm, Dva 这样的优秀框架。而 Fish Redux 站在巨人的肩膀上,将集中,分治,复用,隔离做的更进一步。
2.7 Provide
和Scoped_model一样,Provide也是借助了InheritWidget,将共享状态放到顶层MaterialApp之上。底层部件通过Provier获取该状态,并通过混合ChangeNotifier通知依赖于该状态的组件刷新。
Provide还提供了Provide.stream,让我们能够以处理流的方式处理数据,不过目前还有一些问题,不推荐使用。
2.6 Provider 【推荐】
2019 Google I/O 大会上重磅消息出了支持 flutter_web 之外,另一个便是弃用之前的状态管理 Provide,转而推荐相似的库 Provider;虽然只有一个字母之差使用方式差别却很大;小菜初步学习一下新的状态管理库 Provider;
Flutter 针对不同类型对象提供了多种不同的 Provider;Provider 也是借助了 InheritWidget,将共享状态放到顶层 MaterialApp 之上;
3 实际开发中遇到的坑
3.1 ⚠️Provider中如何抉择 Consumer 还是 Selector
当我们需要更新页面的时候,我们会用notifyListeners();
但是如果我们用Consumer来包裹一个listview我们打印会发现已经build过的item都会重新的build一次。
其实这样有一点不合理,因为我们只需要更新的是其中一个item。
这时候如果我们用Selector来处理的话就会发现大有不同。它只会build我们需要更新的那个item这样的话就更加合理。
但是使用Selector的话,需要注意的是shouldRebuild用什么来决定是否重新build
3.2 ⚠️Provider使用Provider.of(context) 获取顶层数据的坑
如果我们APage使用Provider.of(context) 获取顶层数据,然后BPage对数据进行了更改,其实会影响到APage的然后会重新运行其 build。
其实我们command点击of进到底层代码发现,除了context以外还有一个参数listen。
如果我们在APage设置listen:false,这样就不会因为BPage的操作影响到APage了
3.3 ⚠️SingleTickerProviderStateMixin与TickerProviderStateMixin的坑
当使用vsync: this的时候,State对象必须with SingleTickerProviderStateMixin或TickerProviderStateMixin
首先我们要搞清楚SingleTickerProviderStateMixin于TickerProviderStateMixin的区别:
TickerProviderStateMixin适用于多AnimationController的情况
SingleTickerProviderStateMixin适用于单个AnimationController的情况
其实非必须用TickerProviderStateMixin,建议是用SingleTickerProviderStateMixin的。
1.因为如果 APage 使用 with TickerProviderStateMixin,当从APage 跳转到BPage的时候或从BPage返回到APage的时候你打印会发现都调用了build方法。
2.如果不使用 with TickerProviderStateMixin,当从APage 跳转到BPage的时候或从BPage返回到APage的时候都不会调用了build方法。
参考来源:
State management
Flutter移动应用:状态管理
Flutter | 状态管理指南篇——Provider
八种 Flutter 状态管理-深入评论