当前位置:   article > 正文

Agora Talk 回顾 | 深入聊聊不一样的 Flutter

声网 flutter 用户

9 月 9 日,第 17 期 Agora Talk 邀请到了《Flutter开发实战详解》的作者郭树煜(Github ID:CarGuo) 。熟人都会叫他大喵。他在直播中与大家分享了一些 Flutter 上有意思的话题,包括 Flutter 的现状、Widget、Element、混合开发等。本文由他整理自本次的演讲。大家也可以点击「阅读原文」观看视频回放。

一、Flutter 的现状和钱途

之所以聊这个话题,因为比起后面的部分可能大家会更关心这个,Flutter 自出道以来可以说是突飞猛进,因为它独特的底层设计和开发语言,但是作为一个跨平台框架它也是备受追捧和争议。

问题回答放到了最后面

争议

欲扬先抑,在说好处之前先聊下它的争议,作为新的东西出来总是有人安利有人抵触,这是正常的现象,特别是这里面涉及到学习成本的问题,所以 “Flutter 垃圾,不用学” 的言论也遇到过不少,不过抛开带节奏的,这里面吐槽最多的应该就是 :

  • Flutter 嵌套恶心;

  • 使用 dart 不使用 js ;

这两个确实是最多的争议,无可厚非对比 js 帝国 dart 确实显得“弟弟”,而且 Flutter 的嵌套写法确实不讨喜。但是这个其实可以解释过去,我们后面会解释为什么 Flutter 可以这样嵌套,要怎么去理解和使用这个嵌套。

另外 Flutter 在混合开发上的难度确实会比 RN 更麻烦一些,这是它天生的跨平台设计造成的,Flutter 最核心的优势就是在于它不走寻常路的跨平台模式,简单来说对于平台而言,只需要平台提供画板(Surface)就可以了,剩下都是由 Flutter Engine 来完成绘制,这让 Flutter 的 UI 和平台的关联性很低,但是也造成了 Flutter 在混合开发时需要接入原生控件的难度。

另外 Flutter 在第三方支持和内存占用上的优势目前也不够明显,当然目前 pub 上的第三方插件其实已经比较丰富了,大部分时候能满足开发需求。另外就是内存占用上会多一点,当然这个内存占用属于 Native 的,也就是不会因为超过 JVM 的内存限制而导致 OOM 的问题,不过会比平时的应用起步占用的内存更多一点。

最后就是热更新,Flutter 的热更新就不要想了,至少是官方的热更新或者是直接下发二进制代码的支持就不用想了,因为无论是从谷歌或者苹果的角度出发,这都是禁止的底线。当然现在有各种通过下发文本或者模版去实现类似 code-push 的热更新,Android 平台也可以用动态化的方式下发,因为 Android 上 Flutter 在 release 模式是打包成 so 动态库的,当然这就是另外一个话题了,它究竟好不好我就不评价了~

优势

说完争议我们聊聊优势,现实中没有什么框架是完美的,大部分时候主要在于它能不能解决你的需求,从我这两年多使用 Flutter 的感觉上来说,Flutter 给我最大的感觉就是舒服!

过去 5 年里在跨平台相关领域我使用过 Cordova、 RN、Weex 等框架,他们无疑是提高了我在某些场景下代码的复用,但是在 UI 的兼容和性能上总是需要耗费额外的精力来处理一些琐碎的问题。

对比以前在 RN 和 Weex 上,时不时遇到:“在 Android 端调整完样式后,在 iOS 端不生效或者异常的情况”,这是因为 RN 等框架需要依赖原生控件,而原生的控件在不同版本和平台上都存在一定的差异化。

Flutter 得益于它特有的跨平台渲染机制,在 UI 兼容性和代码复用上 Flutter 让我感觉很舒服,因为框架自带的 Widget 渲染是平台无关的,基本上调样式在一个平台上适配完了,另外的平台也不会有什么问题。

这也进一步提高了代码的复用率。

Flutter 在 release 模式下的性能体验还很不错,记住这里重点记得是 release 而不是 debug,并且不是在虚拟机上的性能,这个官方也提示过。

关于性能对比的部分可以在我的掘金或者公众号也有相关的文章。

说起这个聊一个题外话,关于跨平台框架存在的意义,我以前在面试的时候就遇到过面试官这样的问题:我本身就会 Java 和 Objective-C, 直接写各平台的代码不是更好吗?当然好,这样的性能和兼容性肯定最有保证,但是逻辑上是冗余的,跨平台的主要优势在于代码逻辑的复用,减少各平台同一逻辑,因人或者因时而异的开发成本,也可以不同平台开发人员的扯皮时间

当然我相信每一个流行框架在性能上都不会有太大问题,更多的是使用框架的场景和方式对不对,另外 Flutter 其他的优势在于官方对于 Web 、 Linux 、Win、MacOS 平台的支持和推进速度也很快。

钱途

至于 Flutter 现在在国内投入使用的现状,大家可以看看我在6月份时发过的一篇文章: 《国内大厂在移动端跨平台的框架接入分析》文章分析了 53 个大厂的应用样本,其中发现有 Flutter 使用的有近 19 个,这里面还不包括使用了动态化下发的。

全球范围内的话,谷歌年初统计 Flutter 使用者数量排名前五的地区是印度、中国、美国、欧盟和巴西,所以本质上在争议中 Flutter 的普及还是很快的。

另外目前各大平台的内容产出,相信大家也可以关注到字节跳动、阿里、美团等平台都在 Flutter 上都有对应的投入,同时 Flutter 如今除了 Android 和 iOS 平台之外,各大厂对于 Web 、MacOS 、Linux 、Win平台的支持也在推动,因为 Flutter 在类似小程序、2B的商业应用场景上它很实用,之前在各种渠道上和钉钉、字节等相关的技术人员有过接触,他们都在 Flutter 跨多平台上有着各种各样的需求,大家可以相信 Flutter 目前的钱途还是可以放心的。

这里要补充一点:纯 Flutter 的岗位还是比较少的,它更多是作为一项加分或者竞争项的能力存在,因为跨平台的应用解决的大部分是 UI 上的问题,剩余的很多涉及平台的逻辑还是需要所在平台的能力去解决。妄想一劳永逸的跨平台现阶段是看不到的,各个平台也不会允许一个第三方的框架独霸,就比如目前微软在 Flutter 平台的支持是就不是很积极,所以如果看到什么 “Flutter 一统天下,xxx要凉” 的文章,大家看个乐就好了。

那接下里就开始介绍 Flutter 本身的一些有意思的设定。

二、Flutter 的表世界:Widget

相信了解过 Flutter 的这张图大家一定很熟悉,可以看到作为 UI 框架 Flutter 抽象出了很多层来实现从平台到开发过程中的解耦。当然日常普通开发我们只会关心 Widget ,而 Widget 上的 Material 和 Cupertino 是官方为我们封装的不同风格的 Widget ,这些控件基本上都是和平台无关的

那要理解 Flutter 就要理解 Flutter 中最灵魂的设定:

Flutter 内一切皆 Widget ,Widget 是不可变的(immutable),每个 Widget 状态都代表了一帧。

理解这段话是非常重要的。

这句话也是很多一开始接触 Flutter 的开发者比较迷惑的地方,因为 Flutter 中所有界面的展示效果,在代码层面都是通过 Widget 作为入口开始。 Widget 是不可变的,说明页面发生变化时 Widget 一定是被重新构建, Widget 的固定状态代表了一帧静止的画面,当画面发生改变时,对应的 Widget 一定会变化。

是不是有点绕了?

怎么理解呢,举个简单例子,如下代码所示定义了一个 TestWidgetTestWidget 接受传入的 title 和 count 参数显示到 Text 上,同时如果 count 大于 99,则只显示 99。

  1. /// Warnning
  2. /// This class is marked as '@immutable'
  3. /// but one or more of its instance fields are not final
  4. class TestWidget extends StatelessWidget {
  5. final String title;
  6. int count;
  7. TestWidget({this.title, this.count});
  8. @override
  9. Widget build(BuildContext context) {
  10. this.count = (count > 99) ? 99 : count;
  11. return Container(
  12. child: new Text("$title $count"),
  13. );
  14. }
  15. }

这段代码看起来没有什么问题,也可以正常运行,但是在编译器上会有 “This class is marked as '@immutable',but one or more of its instance fields are not final”的提示警告,这是因为 TestWidget 内的 count 成员变量没有加上 final声明,从而在代码层面容易产生歧义。

因为前面说过 Widget 是 immutable ,所以它的每次变化都会导致自身被重新构建,也就是 TestWidget 内的 count 成员变量其实是不会被保存且二次使用。

如上所示代码中 count成员没有 final 声明,所以理论是可以对 count 进行二次修改赋值,造成 count 成员好像被保存在 TestWidget 中被二次使用的错觉,容易产生歧义,比如某种情况下的widget.count,所以这个 final就可以看出来 Widget 的不可变逻辑。

那这时候可能有人会问:每次 Widget 都重构,那怎么保存状态?这样 Widget 的设计会不会性能很差?

我们先讲状态保存,把 StatelessWidget 换成 StatefulWidget ,然后把 build 方法放到 State 里,State 里的 count 就可以就可以实现跨帧保存。

  1. class TestWidgetWithState extends StatefulWidget {
  2. final String title;
  3. TestWidgetWithState({this.title});
  4. @override
  5. _TestWidgetState createState() => _TestWidgetState();
  6. }
  7. class _TestWidgetState extends State<TestWidgetWithState> {
  8. int count;
  9. @override
  10. Widget build(BuildContext context) {
  11. this.count = (count > 99) ? 99 : count;
  12. return InkWell(
  13. onTap: () {
  14. setState(() {
  15. count++;
  16. });
  17. },
  18. child: Container(
  19. child: new Text("${widget.title} $count"),
  20. ),
  21. );
  22. }
  23. }

到这里我们知道了 Widget 在 Flutter 是不可变的, Widget 内的变量也不会被保存,并且需要用 final 声明避免歧义。而用了 State 之后就支持跨帧保存数据,那为什么 State 就可以跨帧保存呢? createState 得到的 State 为什么就可以跨帧?

其实看到前面代码里的 context 了吗?和它有密不可分的关系。

三、Flutter 的里世界:Element

事实上如果查看 Flutter 对于 Widget 的定义,可以看到官方对于 Widget 的定义是 Describes the configuration for an [Element]. ,也就是 Element 才是 Widget 真正的体现,Widget 更像是配置文件的逻辑,也就是我们通过嵌套 Widget 去配置想要显示的逻辑,然后 Flutter 根据配置文件去进行渲染,通过配置文件变了,所以配置文件可以经常改变,只要真正工作的节点不要频繁重建就好了

这时候你再想想 Widget 的嵌套,em·····所以 Flutter 上大部分时候我们写的是都是配置代码?其实看 Flutter 里的 Widget 默认都是很简单的功能,就像是一个个简单的配置单元,它并不是什么真实的 View ,真正干活的是从 Element 开始

而 Element 大家可能会比较陌生,因为 Flutter 开发中比较少直接使用 Element。但是如果说到 BuildContext 相信大家就不会陌生,因为 BuildContext 在开发中经常被使用到,而 Element 恰好就是 BuildContext 的实现类,你使用的 BuildContext 其实就是 Element,所以日常开发中其实大家一直都会使用到  Element

就比如正常我们使用 StatelessWidget 和 StatefulWidget 都有它对应的 Element ,而 StatelessElement 和 StatefulElement 都属于 ComponentElement, 也就是容器,这种 Element 的特点是没有 RenderObject,就是它们没有绘制功能,所以我们开发中一种用它们去作为承载容器,然后在 build 方法里去组织我们的 UI ,其实可以看到 build 方法里的 context 就是 this ,也就是 Element 自己。

所以为什么State 可以跨帧保存数据,就是因为它和 Element 的关系!

一个 Widget 被首次加载时,会先创建出它对应的 Element,就比如上面介绍的  StatefulWidget,会对应先创建出 StatefulElement;而 StatefulElement 被调用的时候,会先通过 widget.createState() 创建出 State ,之后 _state 就被保存在 Element,所以 _state 可以跨 Widget 保存,保存的位置就是在 Element 里面。

所以 StatfulWidget 的 build 方法不在 Widget 里而在 State 里面。

在简化的源码里可以看到, _state 就被保存在 Element 中,然后 Widget 是在创建和更新时被赋予到 state 里的,所以 _state 可以跨帧保存,而 Widget 可以不断创建。所以 State 具备声明周期,而 Widget 不具备

理解了吗?Element 才是一个真正的工作节点,而 Widget 仅仅是一个“配置信息”。而 State 是跟着 Element 走,保存在  Element 中,所以它可以用于保存和恢复状态

因为 Widget 仅仅是“配置信息” ,所以它可以每次改变时都被重构,而 Element 才是真正的节点,它每次读取 Widget 的状态去配置和绘制,但是 Element 并不是每次都重新创建,而是在 Widget 首次被加载时通过 createElement 创建出来。

什么是首次加载呢?通俗点来说就是这个 Element 在 mount 的时候

举一个例子,如下面代码所示:

  • 在 DemoPage 中通过 Scaffold 的 body 嵌套了 StatePage;

  • 给 StatePage 传递了 data 为 “init” 的字符串,然后通过 _StatePageState 的构造方法传递 data ,此时页面正常显示;

  • 当用户点击 floatingActionButton 时,会执行 setState 触发页面更新,并且把 _DemoPageState内 的 data 改变为 “Change By setState”

  • 但是最终结果 StatePage 界面依然显示 “init”

  1. class DemoPage extends StatefulWidget {
  2. @override
  3. _DemoPageState createState() => _DemoPageState();
  4. }
  5. class _DemoPageState extends State<DemoPage> {
  6. String data = "init";
  7. @override
  8. Widget build(BuildContext context) {
  9. return Scaffold(
  10. /// 将data数据传递给StatePage
  11. body: StatePage(data),
  12. floatingActionButton: FloatingActionButton(
  13. onPressed: (){
  14. /// 改变data数据
  15. setState(() {
  16. data = "Change By setState";
  17. });
  18. },
  19. ),
  20. );
  21. }
  22. }
  23. class StatePage extends StatefulWidget {
  24. final String data;
  25. StatePage(this.data);
  26. @override
  27. _StatePageState createState() => _StatePageState(data);
  28. }
  29. class _StatePageState extends State<StatePage> {
  30. String data;
  31. data是从StatePageState的构造方法传入
  32. _StatePageState(this.data);
  33. @override
  34. Widget build(BuildContext context) {
  35. return Container(
  36. child: new Center(
  37. child: new Text(data ?? "")
  38. ),
  39. );
  40. }
  41. }

为什么 StatePage 界面依然显示 “init”?因为虽然 StatePage 这个 Widget 的 data 发生了改变,但是 StatePage 的 createState() 方法只在第一次被加载时调用,对应创建出来的 state 被保存在 Element 中,所以 _StatePageState 中的 data 只在第一次时被传递进来。

关键就在于 _StatePageState 创建时 data 只被赋数一次。

如果这时候要让 StatePage 的 data 正常改变,就需要使用 widget.data 。看出来了没,如果把 State 放到 Element 的级别上来看,通过  widget.data 去更新的逻辑,是不是 Widget 看起来就很像是一个配置文件

所以对应前面的源码,是不是对应上了,State 保存在 Element 中,而 Widget 被不断更新到 State 里,所以 State 里用 widget.xxxx 可以获取到每次更新的状态

也就是在写 Flutter 时, 把 Widget 当作配置文件写就对了。

介绍了 Widget 和 Element,那最后在问一个问题:Widget 和 Element 的对应关系是怎么样?

四、Flutter 的里表关系

从官方的注释里可以看出,Widget 做为配置文件,和 Element 是应该是一对多的关系,因为真实工作的其实是 Element 树,而 Widget 作为配置文件,可能会在多个地方被复用。这个理解是不是感觉很奇怪?换个角度解释下:一个配置文件被多个运行的实体引用。

举个例子,如下代码所示,通过运行后可以看到 textUseAll 在多个地方被 inflated 并且正常渲染出 3333333 的效果。

这是因为 textUseAll 仅仅是作为配置文件,所以被多个 Element 加载并不会出现问题,因为这种情况并不是一个真正的 "View" 被多个地方添加。

  1. final textUseAll = new Text(
  2. "3333333",
  3. style: new TextStyle(fontSize: 18, color: Colors.red),
  4. );
  5. class MyHomePage extends StatelessWidget {
  6. void goNext(context) {
  7. Navigator.of(context).push(MaterialPageRoute(builder: (context) {
  8. return ShowPage();
  9. }));
  10. }
  11. @override
  12. Widget build(BuildContext context) {
  13. return Scaffold(
  14. appBar: AppBar(),
  15. body: Center(
  16. child: Container(
  17. color: Colors.blue,
  18. height: 80,
  19. child: Stack(
  20. children: <Widget>[
  21. new Center(
  22. child: Row(
  23. crossAxisAlignment: CrossAxisAlignment.center,
  24. textBaseline: TextBaseline.alphabetic,
  25. mainAxisAlignment: MainAxisAlignment.center,
  26. children: <Widget>[
  27. textUseAll,
  28. Text(
  29. ' GSY ',
  30. style: TextStyle(fontSize: 36, fontFamily: "Heiti"),
  31. ),
  32. textUseAll,
  33. ],
  34. ),
  35. ),
  36. ],
  37. ),
  38. )),
  39. floatingActionButton: FloatingActionButton(
  40. onPressed: () {
  41. goNext(context);
  42. },
  43. tooltip: 'Next',
  44. child: Icon(Icons.add),
  45. ),
  46. );
  47. }
  48. }
  49. class ShowPage extends StatelessWidget {
  50. void goNext(context) {
  51. Navigator.of(context).push(MaterialPageRoute(builder: (context) {
  52. return ShowPage();
  53. }));
  54. }
  55. @override
  56. Widget build(BuildContext context) {
  57. return Scaffold(
  58. appBar: AppBar(),
  59. body: Container(
  60. child: new Center(
  61. child:
  62. textUseAll,
  63. ),
  64. ),
  65. floatingActionButton: FloatingActionButton(
  66. onPressed: () {
  67. goNext(context);
  68. },
  69. tooltip: 'goNext',
  70. child: Icon(Icons.add),
  71. ),
  72. );
  73. }
  74. }

当然,当你给上面的  textUseAll 的 Text 配置上 GlobalKey 就是会是另外一个故事,因为  GlobalKey 会让一个 Widget 变成“全局单例” ,具体解释这里就不展开,感兴趣的可以自己尝试下。

最后补充一点: Flutter 中除了 Widget 、Element 之外,还有 RenderObjectLayer 等才能组成 Dart 层的渲染闭环,这里主要介绍了 Widget 和 Element 的关系,是因为 Widget 和 Element 的理解是 Flutter 中入门时最容易忽略的,Flutter 对于 Widget 的设计让我们写代码时更像是在写配置文件

不是所以 Element 都有 RenderObject ,比如前面说过的 CompmentElement

其中最具备代表性的就是 Container ,官方给我们做了一个很通用的配置模板,通过配置通用模版和业务模板,是我们减少嵌套的写法,这可能有点傻,但是这就是 Flutter 的组织方式之一。另一方面讲因为 Widget 本身很轻,作为配置文件前期也不要太担心嵌套性能,因为 Flutter 里的 Widget 很多时候工作逻辑很单一,比如一个  Aligin 到绘制层也就是改变一下绘制时的位置而已。

所以在使用 Flutter 时,对于Widget 的理解其实不应该把它看成 View ,而是应该把它当作配置文件看待;而理解一个 Widget 的实现逻辑,则是应该看它的 Element 和 RenderObejct

当然,虽然通过前面的介绍我们知道 build 方法很轻和 Widget 很轻,但是没必要的调用肯定是能省则省!那么怎么省呢?那就是通过 BuildContext ,因为 BuildContext 就是 Element 的抽象,说白了就是利用 ComponentElement 这种容器来隔离,在更新时减少节点的判断逻辑,从具体上说也可以使用 Builder 等简单的控件来实现,通过 BuildContext 就可以更新的颗粒度,比如状态管理框架 provider 中的 Consumer 。

五、Flutter 中混合开发的变迁史

介绍完 Flutter 基本的设定理念,接下来结果一个 Flutter 中的混合开发的问题,也就是 PlatformView ,之所以介绍这个,是因为如果想要在 Flutter 上接入原生平台的界面能力,比如 WebViewMapView 等等就会需要使用上它,因为 Flutter 本身是通过 Flutter Engine 去绘制控件的,也就是它不像 RN 一样可以快速接入原生平台的控件能力。

如果想要把原生控件直接干涉到 Flutter 的渲染树里是不可能的,因为它们已经是两个完全不同的东西,Flutter 对于原生而已就像是一个单页面应用,原生只提供了一个 Surface,剩下的由 Flutter 自己去绘制。

起初在实现时 Flutter 在 Android 和 iOS 平台采用了两种不同的实现模式,得益于 iOS 平台本身的优势, PlatformView 在 iOS 上的问题一直不是特别多,但是在 Android 平台上,因为使用的是 VirtualDisplay 这种实现方式:简单来说,就是原生控件的内容被绘制到内存里,然后 Flutter Engine 通过相对应的 textureId 就可以获取到控件的渲染数据并显示出来。 

通过从 VirtualDisplay 输出中获取纹理,并将其和 Flutter 原有的 UI 渲染树混合,使得 Flutter 可以在自己的 Flutter Widget tree 中以图形方式插入 Android 原生控件。

从而引发的就是性能、触摸事件还有最最最多问题的键盘输入,特别是 WebView自己内部还有创建和设置输入连接的逻辑,所以 WebView 和键盘的相关逻辑就显得十分脆弱,经常闹幺蛾子,如果对这部分感兴趣的可以看我写过的《Android PlatformView 和键盘问题》

而在 1.20 开始,Android 平台的 PlatformView 多了 Hybrid Composition 模式,这个模式就和 iOS 本身的实现很相似了,他更好的解决了键盘和触摸等玄学问题。

在 Hybrid Composition 上新增了一个对象:FlutterImageViewHybrid Composition 上混合原生控件所需的图层合成就是通过 FlutterImageView 来实现。FlutterImageView 本身是一个普通的原生 View, 它通过实现了 RenderSurface 接口从而实现如 FlutterSurfaceView的部分能力。

默认情况下添加的 PlatformView 就是把控件直接添加到现在 Flutter 的上面,其实这并没有什么高大上的,就是在原生的 FlutterView上面 addView 而已。但是当你在  PlatformView 的上面继续覆盖一个 Flutter 自己渲染的控件的话,那么 FlutterImageView 就出场了。

通过  FlutterImageView 实现了 Surface 和 Surface 之间的堆叠,其中  FlutterImageView 的 Surface 来自于ImageReaderImageReader 可以简单理解为就是能够存储 Image 数据的对象。

而如果他们不在一个区域内,那么就会各自使用自己的 FlutterImageView ,如果在一个区域内,就会合并渲染逻辑,其实效果上就是不同的 Surface 嵌套了堆叠了。

感兴趣的可以看《Flutter 1.20 下的 Hybrid Composition 深度解析》

六、最后

最后再聊聊开源和学习,要如何去学习 Flutter 。

因为我也有几个维护了好些年的开源项目,我发现很多问题 60% 都可以通过文档解决,剩下 30% 能通过源码解决,在播放器项目上我就遇到过好多有拿着 html 链接问我为什么播放器不能播放的问题,或者基本是支不支持某个 API,这类问题其实是最多的。

所以说阅读源码很重要,源码才是开源项目的灵魂,如果只停留在 API 使用阶段,那么就会发现怎么用都不顺手,这也是开发者会感觉觉一字型螺丝刀拧十字螺丝时好时坏的逻辑。

学 Flutter 也是,其实 Flutter 本身并不复杂,理解了它的 Widget 和 Element之间的关系,甚至后面 RenderObject 和 Layer 的关系,之后通过源码你就会发现很多需求和问题很好解决了。虽然面向单纯的 API 编程会比较舒服,当时阅读源码这个习惯也会成为一个良好的财富,是如何把别人的东西变成你的能力,而不是拿着别人的螺丝刀无从下手的虚无感。

问题

1、Flutter 后面是否会有UI可视化编辑工具。

有的,Hot UI 就是这样的功能,从1.12开始 Flutter 官方就开始开发这样的工具,在官方的 HotUI-Getting-Started-instructions 中可以看到相关的描述:This feature is currently experimental. To enable, go to Preferences > Languages & Frameworks > Flutter Then check "Enable Hot UI" under "Experiments". 目前该功能还处于实验阶段,在 Android Studio 的设置中,如图所示底部勾选启动这个功能,但是对于稳定版目前预览的 Screen mirror 处于 coming soon 的状态

2、Flutter 为什么性能好。

Flutter 性能好,是因为它的跨平台实现方法不需要桥接原生控件,就如同游戏引擎,Flutter 界面都是由 Engine 绘制,而 Engine 绘制使用的 Skia 工具, Android 原生也是使用 SKia 绘制,所以它们的性能会很接近。

3、Flutter Web 和交互

Flutter 其实在 Web 实现上是存在两种 Canvas 实现 :DomCanvas 和 CanvasKi ,

DomCanvas 用 Dom 来模拟 OpenGL的绘制,而 CanvasKit 是使用 WebAssembly 和 WebGL 在浏览器中渲染 Skia 绘制命令,后者性能更好但是兼容性还有待商榷,所以目前 Web 可能还是会 Dart2JS 的实现比较多。

另外 dart:js library 是一套底层支持 Dart 和 JS 交互的官方库,支持 Web 上 Dart 和 JavaScript 的交互处理。

4、Flutter 只能处理 UI 逻辑?

不是这么说,只是说 Flutter 主要是解决了 UI 的跨平台,但是不能说 Flutter 只能写 UI ,很多逻辑代码也可以用 Dart 写在 Flutter 内,只是 Dart 和 JS 一样都是单线程的,如果太过复杂的操作,建议用isolate 去处理。

另外涉及平台的硬件或者特性的某些功能,只能通过插件去解决了。

5、讲讲 Fish Redux

其实 Fish Redux 是在 Redux 的基础上进一步增加了细致化和可配置化的能力,它相对会复杂一点,但是提高了服用率和控制的细腻度,但是问题也在于它的入侵性比较强,接入后基本上后期你想更换状态管理框架会比较麻烦,所以看个人取舍了。

6、Flutter 为什么对比其他框架性能会提升

最主要原因是 Flutter 的不需要桥接原生控件去渲染,它直接使用把绘制信息提交给 Skia 绘制出来,而原生 Android 也是通过 Skia 绘制,所以本质上大家的流程是一致的,所以性能上会更加接近原生。另外在 iOS 上 Flutter 在 iOS 上对于支持 Metal 的设备将使用 Metal 进行渲染s。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/170376
推荐阅读
相关标签
  

闽ICP备14008679号