Skip to content

Flutter 后台运算与大量文本渲染

Published: at 13:24

最近又重拾旧业开始捣鼓 Flutter,这次想做的是 SC 相关的一个应用,其中有一个需求是显示大文本。

其实单纯的显示大文本并不难解决,但是问题在于显示大文本之前有一段解密的过程混淆了我的视听耽误了一晚上的时间。于是正好借着这个机会了解了一下 compute 和大量文本渲染的 Workaround,算是这次的收获之一了(笑)。

后台运算

与其他单线程的家伙们类似,Flutter/Dart 的日常生活也是单线程的。对于日常使用来说这样倒是无所谓,但一旦计算多了,就会影响到 UI 的绘制了。

经典的加载场景是一个加载条(对于 Material Design 来说通常是圆形的)在加载完之前不断前进不要停下来啊,在加载完之后显示数据。对于涉及到大量运算的操作,我们同样希望能够有一个 UI 让用户等待,同时在计算的过程中不要阻碍 UI 的渲染。

符合我们需求的就是 compute

compute

computeDart isolate 的一部分,相比与 isolate,它更简单易用。compute 表现为一个 async function,接收两个参数,一个是调用的函数名,另一个是调用函数的参数

由于 isolate,因此 compute 和外界的数据是隔离的,因此需要将使用到的数据全部传入才能正常工作;同时,由于 compute 只接收一个参数,因此对于多个参数的情景需要用到 {};最后,同样是隔离的原因,compute 只能接收 static 或者顶层(不在 class 中的)函数。

最后举一个例子吧:

1
String decrypt(Uint8List buffer) {
2
var key = "here_is_key"
3
.split("")
4
.map((e) => e.codeUnitAt(0))
5
.toList();
6
int index = 0;
7
var arr = buffer.map((value) => value ^ key[index++ % key.length]).toList();
8
return utf8.decode(arr);
9
}
10
11
var resp = await http.get(widget.link);
12
var data = await compute(decrypt, resp.bodyBytes);

这是一个简单的 XOR 加密的解密过程。我们使用了 compute。在实际应用中,我们还可以将其与 FutureBuilder 一起使用。

大量文本渲染

大量文本渲染是另一个非常耗费资源的事情。以 go-flutter 为例,渲染 1~2 MB 的压缩后 JSON 需要使用近 5G 的内存,非常恐怖。

并且,由于是 UI 渲染行为,因此必定会阻塞 UI 线程,最后导致的就是应用崩溃。

Flutter 有对应的 [issue](https://github.com/flutter/flutter/issues/32338),但目前尚未修复,因此我们需要使用一些 Workaround。这里我使用的是分行 ListView 的解决方案,也就是 issue 中的 Workaround 1

我们都知道,ListViewlasyload 的,因此将大量的 TextListView 渲染就可以达到让文本本身 lazyload 的效果。如下所示:

1
// ...
2
return FutureBuilder(
3
future: () async {
4
var resp = await http.get(widget.link);
5
return = await compute(decrypt, resp.bodyBytes);
6
}(),
7
builder: (context, snapshot) {
8
if (snapshot.connectionState != ConnectionState.done) {
9
return CircularProgressIndicator();
10
}
11
12
return ListView(
13
children: [
14
...snapshot.data.split("\n").map((line) => Text(line)).toList()
15
],
16
);
17
},
18
);

最后的效果如下:也算是完美解决了吧(笑)(除了不能选中(