在Flutter中,如果我们想要更新页面中的某个widget的状态的话,一般会使用setState方法重走build方法来刷新。
//Flutter是描述性的(declarative), UI反映状态.
UI = f(state)
当页面布局复杂的时候,这样肯定是不行的。 这边梳理了下几种常见的方式,做个记录,方便将来选型
总体分为两类:
Flutter里面最重要的方式 setState,规模较小的程序足够了,所有其它方式最终都需要调用 setState。
setState((){
_value = "new data";
});
ValueNotifier 里面包含一个 T value 值,当值改变的时候,会通知它的监听来刷新UI
//初始化
ValueNotifier indexNotifier = ValueNotifier(0);
//数据变更:
indexNotifier.value = value;
//触发UI刷新
ValueListenableBuilder(
valueListenable: indexNotifier,
builder: (BuildContext context, int value, Widget child) {
return CircleCheckBox(
size: 40,
activeColor: Color(periwinkle),
value: _isSelect(value),
onChanged: _onSelect,
);
},
)
//销毁
indexNotifier.dispose();
ValueNofiter 其实跟Android DataBinding 里面的 ObserverField 类似,都是使用一个观察者模式来实现数据驱动UI修改。
使用过程发现,这个类有个bug 当Value 是List是使用,修改数据不会发出通知,需要手动调用 notifyLisener().
搜索了下,Flutter 项目下已经有这个issue了(https://github.com/flutter/flutter/issues/29958) 。 原因是因为,里面判断数据有变更的方法没有对List进行处理。
GlobalKey能够跨Widget访问状态,相比于Flutter的 函数声明式编程 类似 android等其他平台的命令式调用
//初始化:
GlobalKey key = GlobalKey();
//使用:
key.currentState.changeState();
StreamBuilder也是官方内置的一种刷新UI方式。数据封装成 流 通知UI变更
//步骤1:初始化一个StreamController<可以是 int string... 一般是model>
final StreamController _streamController = StreamController();
//发送
_streamController.sink.add(_str);
//UI 变更
child: StreamBuilder( // 监听Stream,每次值改变的时候,更新Text中的内容
stream: _streamController.stream,
initialData: _str,
builder: (BuildContext context, AsyncSnapshot snapshot){
return Text('点击的时候这个值会改变: ${snapshot.data}');
}
),
// 销毁
_streamController.close();
通常用于是 异步编程,Future返回值是情况,它的构造方法:
const FutureBuilder({
Key key,
this.future, //获取数据的方法
this.initialData, //初始的默认数据
@required this.builder
}) : assert(builder != null),
super(key: key);
provider 是 Google I/O 2019也推荐的状态管理库,网上内容也很多,源代码其实不多,很容易看懂。出自 Flutter dev team,绝对的官方了,总共代码 675行
Hilight里面也是用这个进行状态管理
实现:
class CounterModel extends ChangeNotifier {
int value = 0;
void increment() {
value++;
notifyListeners();
}
void decrement() {
value--;
notifyListeners();
}
}
void main() => runApp(ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MyApp(),
));
Consumer(
builder: (context, counter, child) => Text(
'${counter.value}',
),
),
或者:
FloatingActionButton(
onPressed: () =>
Provider.of(context, listen: false).increment(),
),
Provider性能相关的实现细节
return Consumer(
builder: (context, cart, child) => Stack(
children: [
// Use SomeExpensiveWidget here, without rebuilding every time.
child,
Text("Total price: ${cart.totalPrice}"),
],
),
// Build the expensive widget here.
child: SomeExpensiveWidget(),
);
源自 Fuchsia 代码,包括注释也只有 287 行代码。 Scoped Model是基于InheritedWidget的. 思想仍然是把状态提到上层去, 并且封装了状态改变的通知部分。Provider 就是就是这个基础上
再新增 Providers,Provider 支持 Stream
2021 0708 重新回顾了下,发现这个库挺好用的。定点刷新比较简单。可以大力使用:
flutter 一文带你了解GetX
InheritedWidget的主要作用是在Widget树中有效地传递信息.
Flutter中常用的Theme, Style, MediaQuery等就是inherited widget, 所以在程序里的各种地方都可以访问到它们.
InheritedWidget也会用在其他状态管理模式中, 作为传递数据的方法.
当用InheritedWidget做状态管理时, 基本思想就是把状态提上去.当子widgets之间需要共享状态, 那么就把状态保存在它们共有的parent中
InheritedWidget解决了访问状态和根据状态更新的问题, 但是改变state却不太行.