0%

Flutter get框架使用心得

image

Flutter轻量级状态、资源管理框架[get](https://pub-web.flutter-io.cn/packages/get)的使用心得,包含状态更新原理,get_cli
# 什么是get,它有哪些功能 get是最流行的Flutter开发框架之一,包含路由管理、Widget状态管理、资源管理、多语言等,集成了get后,相当于同时拥有了上述功能,页面跳转更加方便快捷,极其好用的状态管理更是开发利器。 # 状态管理 使用get最大的好处就它的状态管理功能,把业务逻辑转移到了controller中,widget的使用只需要包上Obx就可以轻松实现controller中数据变动触发widget的更新

简述状态管理的原理

使用方法如下:

1
2
3
4
child: Obx(() => Text(
controller.testStr.value,
style: const TextStyle(fontSize: 20),
)),

widget监听

当使用Obx包装Widget后,child构建会被传入ObxWidget,ObxWidget是StatefulWidget,内部持有一个RxNotifier,用于观察数据变动后回调给当前的state触发setState更新当前的widget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ObxState extends State<ObxWidget> {
final _observer = RxNotifier();
late StreamSubscription subs;

@override
void initState() {
super.initState();
subs = _observer.listen(_updateTree, cancelOnError: false);
}

void _updateTree(_) {
if (mounted) {
setState(() {});
}
}
......
}

Rx值绑定

child构建会调用相关obs数据类的.value方法,在获取.value时触发绑定

1
2
3
4
5
/// Returns the current [value]
T get value {
RxInterface.proxy?.addListener(subject);
return _value;
}

build中的绑定逻辑

具体的绑定逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
abstract class RxInterface<T> {
static RxInterface? proxy;

bool get canUpdate;

/// Adds a listener to stream
void addListener(GetStream<T> rxGetx);

/// Close the Rx Variable
void close();

/// Calls `callback` with current value, when the value changes.
StreamSubscription<T> listen(void Function(T event) onData,
{Function? onError, void Function()? onDone, bool? cancelOnError});

/// Avoids an unsafe usage of the `proxy`
static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) {
final oldObserver = RxInterface.proxy;
RxInterface.proxy = observer;
final result = builder();
if (!observer.canUpdate) {
RxInterface.proxy = oldObserver;
throw """
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
""";
}
RxInterface.proxy = oldObserver;
return result;
}
}

总结

Obx包装某个Rx变量后,都会把当前的使用到Rx变量与其包装的Obx绑定起来,当Rx变量变化的时候就会触发Obx的setState更新。

使用中的细节点

是否可以用一个Obx包装根widget,下面的widget调用Rx变量就不用Obx包装了?

根据上述绑定原理,这样做是可行的,确实省略了在下面多次包装Obx的步凑。但是,这种方法的缺点是:当下面用到的任何一个Rx变量更新,都会导致根widget的setState触发,导致整个widget树的刷新,所有的widget都会重新走build方法。
很显然这是不合理的,我们应该控制最小范围刷新才符合状态规范,性能最优。所以我们应该明确自己的widget在绑定哪个Rx字段时需要刷新,并明确调用,而不是刷新所有widget。

我使用Obx包装了某个widget,该widget是通过调用一个方法获取的值,方法中使用了Rx的value值,是否可以绑定成功?

可以的,但需要注意获取值的方法应该随build构建同步调用,也就是说在Obx包装的widget中,获取Rx值的函数是一定被调用的才能绑定成功。
如果widget获取值的方法不是立即执行的,例如是通过一个传入方法来获取的,而该传入方法又不会调用,必须依赖其他状态才会触发调用的情况,就有可能不会绑定成功。

我使用Obx包装了一个StatefulWidget,在StatefulWidget的构造函数中调用了Rx变量值,Obx触发绑定失效了吗?为什么Rx值的变动不能触发widget的更新呢?

不是的,Obx的触发并没有失效,不更新的原因也很简单,StatefulWidget的widget更新由state控制,当我们的state创建后,已经使用widget中的属性初始化过了,所以当Rx值变动,触发Obx刷新时,会调用到其包装的StatefulWidget的didUpdateWidget(covariant Test oldWidget)方法判断是否要通过新的widget来更新state,我们没有在widget覆写该方法,则不会触发state内部字段的改变,所以只会触发state的build方法,如果build方法中有引用widget.xxx的值,那么该值是会更新的。

总结

1.Obx的更新会触发其包装widget的更新,包装widget是否更新则由该widget自己来决定。

一个简单的联动方案是给该statefullWidget一个UniqueKey,那么每次触发state的重建,那么视图也就更新了。实际应用中,不推荐使用此方法,因为创建state是消耗资源的,频繁变动的情况,会影响性能和使用体验。

2.尽量使Rx的值变动影响到最小范围的widget重构。

get_cli

使用get框架后,在创建页面的时候也不用手动去添加各种controller,widget,只需要使用其提供的get_cli即可

install get_cli
安装指令

1
2
3
4
5
// To install:
pub global activate get_cli
// (to use this add the following to system PATH: [FlutterSDKInstallDir]\bin\cache\dart-sdk\bin

flutter pub global activate get_cli

创建页面相关指令

1
get create page:home

更多指令请参考get_cli文档说明