带你全面了解 Flutter,它好在哪里?它的坑在哪里? 应该怎么学?

本文探讨了Flutter的性能优势和误区,强调了响应式开发的重要性,尤其是Widget的不可变性。同时指出了混合开发时的成本和性能挑战,以及WebView和图片处理的问题。作者提供了学习Flutter的建议和资源,包括理解Widget背后的概念和避免常见陷阱。
摘要由CSDN通过智能技术生成

当然,Flutter 这样实现也有坏处,那就是当你需要使用平台的控件作为混合开发时,Flutter 的成本和体验无疑被放大 ,这一点上 react-native 反而有着先天的优势。

3、Flutter 的性能

其实前面也介绍过 Flutter 的性能一般情况下是比 react-native 好,关于这个这里主要介绍几个误区:

  • 1、Flutter 在 debug 和 release 下的性能差距是巨大的,因为它们之间是 JIT 和 AOT 的区别。

  • 2、不要在模拟器上测试性能,这个根本没有意义,因为在手机上 Flutter 会更多依赖 GPU 的能力。

  • 3、混合开发 Flutter 是有性能有影响的,比如在原有 Android 项目里,把某个模块业务逻辑改用 Flutter 实现,这对性能和内存会有很大的考验,至于为什么?就是前面说过 Flutter 独立的控件渲染和堆栈管理带来的负面效果。

  • 4、同一个框架在不同人手下会写出不一样的结果,一般情况下对于普通开发者来说,流行的框架一般不会带来很大的性能瓶颈,反而是开发能力比较多导致项目的瓶颈。

怎么学 Flutter ?

当你快速搭建好环境,简单了解 Flutter 的 API 之后,学习 Flutter 在我看来主要有两个核心点:响应式开发和 Widget 的背后是什么?

1、响应式开发

响应式开发相信对于前端来说再熟悉不过,这部分内容对于前端开发来说其实可以略过,响应式编程也叫做声明式编程,这是现在前端开发的主流,当然对于客户端开发的一种趋势,比如 Jetpack Compose 、SwiftUI 。

Jetpack Compose 和 Flutter 的相似程度绝对让你惊讶。

什么是响应式开发呢?简单来说其实就是你不需要手动更新界面,只需要把界面通过代码“声明”好,然后把数据和界面的关系接好,数据更新了界面自然就更新了

从代码层面看,对于原生开发而言,响应式开发中没有 xml 的布局,布局完全由代码完成,所见即所得,同时你也不会需要操作界面“对象”去进行赋值和更新,你所需要做的就是配置数据和界面的关系

举个例子:

  • 以前在 Android 上你需要写一个 xml ,然后布局一个 TextView ,通过 findViewById 得到这个对象,再调用 setText 去赋值;

  • 现在 Flutter 里,你只需要声明一个 TextWidget ,并把 data.title 这样的数据配置给 Text ,当数据改变了, Text 的显示内容也随之改变;

对于 Android 开发而言,大家可能觉得这不就是 MVVM 下的 DataBinding 也一样吗?其实还不大一样,更形象的例子,这里借用扔物线大佬在谷歌大会关于 Jetpack Compose 的分享,为什么 Data Binding 模式不是响应式开发:

因为 Data Binding(不管是这个库还是这种编程模式)并不能做到「声明式 UI」,或者说 声明式 UI 是一种比数据绑定更强的数据绑定,比如在 Compose 里你除了简单地绑定字符串的值,还可以用布尔类型的数据来控制界面元素是否存在,例如再创建另外一个布尔类型的变量,用它来控制你的某个文字的显示:

注意,当 show 先是 true 然后又变成 false 的时候,不是设置了一个 setVisibility(GONE) 这样的做法,而是直接上面的 Text() 在界面代码中消失了,每次数据改变所导致的界面更新看起来就跟界面关闭又重启、并用新的数据重新初始化了一遍一样,这才叫声明式 UI,这是数据绑定做不到的。

当然 Compose 并不是真的把界面重启了,它只会刷新那些需要刷新的部分,这样的话就能保证,它自动的更新界面跟我们手动更新一样高效。

在 Flutter 中也类似,当你通过这样的 turefalse 去布局时,是直接影响了 Widget 树的结构乃至更底层的渲染逻辑,所以作为 Android 开发在学习 Flutter 的时候,就需要习惯这种开发模式,“放弃” 在获取数据后,想要保存或者持有一个界面控件进行操作的想法。另外在 Flutter 中,持有一个 Widget 控件去修改大部分时候是没意义的,也是接下来我们要聊的内容

2、Widget 的背后

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

理解这段话是非常重要的,这句话也是很多一开始接触 Flutter 的开发者比较迷惑的地方,因为 Flutter 中所有界面的展示效果,在代码层面都是通过 Widget 作为入口开始。

Widget 是不可变的,说明页面发生变化时 Widget 一定是被重新构建, Widget 的固定状态代表了一帧静止的画面,当画面发生改变时,对应的 Widget 一定会变化。

举个我经常说的例子,如下代码所示定义了一个 TestWidgetTestWidget 接受传入的 titlecount 参数显示到 Text 上,同时如果 count 大于 99,则只显示 99。

/// Warnning

/// This class is marked as ‘@immutable’

/// but one or more of its instance fields are not final

class TestWidget extends StatelessWidget {

final String title;

int count;

TestWidget({this.title, this.count});

@override

Widget build(BuildContext context) {

this.count = (count > 99) ? 99 : count;

return Container(

child: new Text(“$title $count”),

);

}

}

复制代码

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

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

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

如果把 StatelessWidget 换成 StatefulWidget ,然后把 build 方法放到 State 里,State 里的 count 就可以就可以实现跨帧保存。

class TestWidgetWithState extends StatefulWidget {

final String title;

TestWidgetWithState({this.title});

@override

_TestWidgetState createState() => _TestWidgetState();

}

class _TestWidgetState extends State {

int count;

@override

Widget build(BuildContext context) {

this.count = (count > 99) ? 99 : count;

return InkWell(

onTap: () {

setState(() {

count++;

});

},

child: Container(

child: new Text(“${widget.title} $count”),

),

);

}

}

复制代码

所以这里最重要的是,首先要理解 Widget 的不可变性质,然后知道了通过 State 就可以实现数据的跨 Widget 保存和恢复,那为什么 State 就可以呢?

这就涉及到 Flutter 中另外一个很重要的知识点,Widget 的背后又是什么?事实上在 Flutter 中 Widget 并不是真正控件,在 Flutter 的世界里,我们最常使用的 Widget 其实更像是配置文件,而在其后面的 ElementRenderObjectLayer 等才是实际“干活”的对象。

ElementRenderObjectLayer 才是需要学习理解的对象。

简单举个例子,如下代码所示,其中 testUseAll 这个 Text 在同一个页面下在三处地方被使用,并且代码可以正常运行渲染,如果是一个真正的 View ,是不能在一个页面下这样被多个地方加载使用的。

在 Flutter 设定里,Widget 是配置文件告诉 Flutter 你想要怎么渲染, Widget 在 Flutter 里会经过 ElementRenderObject、乃至 Layer 最终去进行渲染,所以作为配置文件的 Widget 可以是 @immutable,可以每次状态更新都被重构。

所以回到最初说过的问题:Flutter 的嵌套很恶心?是的 Flutter 设定上确实导致它会有嵌套的客观事实,但是当你把 Widget 理解成配置文件,你就可以更好地组织代码,比如 Flutter 里的 Container 就是一个抽象的配置模版。

参考 Container 你就学会了 Flutter 组织代码逻辑的第一步。

同时因为 Widget 并不是真正干活的,所以嵌套事实上并不是嵌套 View ,一般情况下 Widget 的嵌套是不会带来什么性能问题,因为它不是正式干活的,嵌套不会带来严重的性能损失。

举个例子,当你写了一堆的 Widget 被加载时,第一次会对应产生出 Element ,之后 Element 持有了 WidgetRenderObject

简单的来说,一般情况下画面的改变,就是之后 Widget 的变化被更新到 RenderObject ,而在 Flutter 中能够跨帧保存的 State ,其实也是被 Element 所持有,从而可以用来跨 Widget 保存数据。

所以 Widget 的嵌套一般不会带来性能问题,每个 Widget 状态都代表了一帧,可以理解为这个“配置信息”代表了当前的一个画面,在 Widget 的背后,嵌套的 PaddingAlign 这些控件,最后只是 canvas 时的一个“偏移计算”而已。

所以理解 Widget 控件很重要,Widget 不是真正的 View ,它只是配置信息,只有理解了这点,你才会发现 Flutter 更广阔的大陆,比如:

  • Flutter 的控件是从 Elemnt 才开始是真正的工作对象;

  • 要看一个 Widget 的界面效果是怎么实现,应该去看它对应的 RenderObejcet 是怎么绘制的;

  • 要知道不同堆栈或者模块的页面为什么不会互相干扰,就去看它的 Layer 是什么逻辑;

  • 是不是所有的 Widget 都有 RenderObejcetWidgetElemntRenderObejcetLayer 的对应关系是什么?

这些内容才是学 Flutter 需要如理解和融汇贯通的,当你了解了关于 Widget 背后的这一套复杂的逻辑支撑后,你就会发现 Flutter 是那么的简单,在实现复杂控件上是那么地简单,Canvas 组合起来的能力是真的香。

Flutter 是个有坑的框架

最后讲讲 Flutter 的坑,事实上没有什么框架是没有坑的,如果框架完美得没有问题,那我们竞争力反而会越来越弱,可替换性会更高。

这也是为什么一开始 Andorid 和 iOS 开发很火热,而现在客户端开发招聘回归理性的原因,因为这个领域已经越来越成熟,自然就“卷”了。

事实上我一直觉得使用框架的我们并没有什么特殊价值,而解决使用框架所带来的问题才是我们特有的价值。

而 Flutter 的问题也不少,比如:

  • WebView 的问题:Flutter 特有的 UI 机制,导致了 Flutter 需要通过特殊的方式来接入比如 WebViewMapView 这样的控件,而这部分也导致了接入后不断性能、键盘、输入框等的技术问题。

  • 图片处理和加载:在图片处理和加载上 Flutter 的能力无疑是比较弱的,同时对于单个大图片的加载和大量图片列表的显示处理上,Flutter 很容易出现内存和部分 GPU 溢出的问题。而这部分问题处理起来特别麻烦,如果需要借用原生平台来解决,则需要通过外界纹理的方式来完成,而这个实现的维护成本并不低。

  • 混合开发是避免不了的话题:因为 Flutter 的控件和页面堆栈都脱离原生平台,所以混合开发的结果就会导致维护成本的提高,现在较多使用的 flutter_boostflutter_thrio 都无法较好的真正解决混合开发中的痛点,所以对于 Flutter 来说这也是一大考验。

然而事实上在我收到关于 Flutter 的问题里,反而大部分和 Flutter 是没有关系的,比如:

  • flutter doctor 运行之后卡住不动”

  • flutter run 运行之后出现报错”

  • flutter pub get 运行之后为什么提示 dart 版本不对”

  • “运行后出现 Gradle 报错,显示 timeout 之类问题”

  • “iOS 没办法运行到真机上”
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

我搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

img

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值