Flutter 框架全面解析与学习指南

Viewed 0

回顾了这段时间解答关于 Flutter 的各种问题后,我发现很多刚刚接触 Flutter 的开发者对于这个框架存在不同程度的误解。每次重复解释相同内容十分耗时,因此决定撰写这篇文章进行总结,帮助大家更好地理解 Flutter。

Flutter 的起源

Flutter 的诞生源于 Chrome 团队的一场内部实验。谷歌前端团队在简化前端规范后,发现基准测试性能提升了 20 倍,这个偶然的发现促使 Flutter 项目立项。Flutter 基于前端技术诞生,其设计理念较为保守,选择的 Dart 语言也偏向稳健。尽管编程模式和语法带有浓厚的前端色彩,但它最初应用于移动客户端开发。

Flutter 面世时面临一个尴尬的局面:对于原生客户端开发者来说,声明式的开发方式与习惯的命令式编程和代码布局分离模式不同,需要额外学习成本,同时 Flutter 的嵌套结构让人不适。对于前端开发者而言,Flutter 的环境配置复杂,除了 VSCode 和 Flutter SDK,还需配置 Java、Gradle、Android SDK、XCode 等原生平台工具,网络问题也时常出现,而且 Flutter 对原生平台知识的要求较高,嵌套问题同样被诟病。

值得注意的是,Dart 语言的学习成本并不高,对于熟悉 JavaScript 的前端或掌握 Java、Kotlin、Swift 的客户端开发者来说,Dart 语法相对简单。嵌套问题是否严重?我们后续会讨论。

综上所述,Flutter 对前端或客户端开发者都存在一定门槛和心理抵触。那么,是否有必要学习 Flutter 呢?

学习 Flutter 的理由

在许多 Flutter 初学者中,很大一部分是“被迫”使用,因为领导或项目要求,这是最直接的动力之一。除非选择更换工作,否则学习 Flutter 成为必然。

个人竞争力层面

开发领域容易产生技术火热的错觉,尤其是在媒体影响下,“孕妇效应”可能导致认知偏差。去年中旬针对 53 个样本的数据分析显示,Flutter(19)、Weex(17)、React Native(22)在跨平台框架中占据重要位置。统计表明,Flutter 已从小众框架逐步成为主流跨平台开发工具之一。

Flutter 可以提升求职竞争力,但需要注意的是,Flutter 本身只是一个跨平台 UI 框架。理解这一点很重要,可以避免被“贩卖焦虑”。Flutter 支持移动端、Web 端和 PC 端,但作为 UI 框架,它主要解决 UI 和部分业务逻辑的跨平台问题。平台相关功能如蓝牙、交互、数据存储、打包构建等仍需原生支持。

现阶段所有跨平台框架,包括 Flutter、React Native、Weex,定位都是 UI 框架,旨在降低 UI 业务跨平台成本,它们的发展离不开原生平台。如果原生平台衰落,跨平台也就失去意义。因此,Flutter 与原生平台是共生关系,缺乏原生开发经验难以高效使用 Flutter。

Flutter 确实能助力职业发展,通过放大业务开发能力,让你参与更多平台开发。React Native 和 uni-app 等也能提供类似价值,尤其对前端开发者性价比更高。但为什么选择 Flutter?还有一个有趣的点:对于 Android 开发者,学会 Flutter 相当于掌握了 70% 以上的 Jetpack Compose。

Flutter 的一致性

我个人更推荐客户端开发者学习 Flutter,因为对前端来说 React Native 和 uni-app 可能更划算。但领导们往往不这么认为。Flutter 的额外优势在于性能和一致性。

Flutter 是真正的跨平台 UI 框架!与 React Native 和 Weex 不同,Flutter 的控件不依赖原生控件渲染,而是由 Flutter Engine 提供平台无关的渲染能力。简单来说,原生平台提供画板,Flutter 负责渲染控件,最终打包为 AOT 二进制。

因此,Flutter 的 UI 控件能做到所见即所得,这是一个重要进步。相比之下,React Native 将 JavaScript 控件转化为原生控件渲染,导致不同系统、版本间的控件差异,带来维护成本。在 React Native 开发中,常遇到 iOS 和 Android 样式不一致、属性不支持等问题,虽可通过条件判断和自定义控件解决,但违背跨平台初衷。

Flutter 的控件特性避免了这些问题,我经常只在 iOS 模拟器上开发测试,无需担心 Android 兼容性,尽管屏幕适配仍需处理。从这个角度,Flutter 类似轻量级游戏引擎,提供 2D 控件。

然而,Flutter 的实现也有缺点:当需要混合开发使用平台控件时,成本和体验问题被放大,React Native 在这方面有先天优势。

Flutter 的性能

Flutter 性能通常优于 React Native,但有几点误区需注意:

  • Flutter 在 debug 和 release 模式下性能差距巨大,因为 JIT 和 AOT 的区别。
  • 在模拟器上测试性能无意义,手机上 Flutter 更依赖 GPU。
  • 混合开发对性能有影响,在原有原生项目中集成 Flutter 模块会考验性能和内存,源于 Flutter 独立的控件渲染和堆栈管理。
  • 同一框架在不同开发者手中表现不同,流行框架本身很少成为瓶颈,更多是开发能力导致项目问题。

怎么学 Flutter?

快速搭建环境并了解 API 后,学习 Flutter 有两个核心点:响应式开发和 Widget 的本质。

响应式开发

响应式开发对前端开发者很熟悉,可略过这部分。响应式编程即声明式编程,是前端主流趋势,也是客户端开发的方向,如 Jetpack Compose 和 SwiftUI。Jetpack Compose 与 Flutter 的相似度令人惊讶。

响应式开发的核心是无需手动更新界面,只需通过代码声明界面,并建立数据与界面的关系,数据变化时界面自动更新。从代码层面看,原生开发中没有 XML 布局,布局完全由代码完成,所见即所得,且不需操作界面对象进行赋值和更新,只需配置数据与界面的关系。

例如,以前在 Android 中需编写 XML 布局 TextView,通过 findViewById 获取对象,再调用 setText 赋值;在 Flutter 中,只需声明 Text Widget 并配置 data.title,数据改变时 Text 显示自动更新。

对于 Android 开发者,这可能类似 MVVM 下的 DataBinding,但响应式开发更强。声明式 UI 中,布尔变量可控制界面元素是否存在,当变量变化时,不是设置 setVisibility(GONE),而是界面代码中对应部分直接消失或出现,每次数据改变都像界面重启并用新数据初始化,但实际只刷新必要部分,保证高效。

在 Flutter 中,通过布尔值布局直接影响 Widget 树结构和渲染逻辑。Android 开发者需习惯这种模式,放弃在获取数据后保存或持有一个界面控件进行操作的想法。在 Flutter 中,持有一个 Widget 控件修改大多无意义,这是接下来要讨论的内容。

Widget 的背后

Flutter 中一切皆 Widget,Widget 是不可变的,每个 Widget 状态代表一帧。理解这句话至关重要,因为 Flutter 中所有界面展示都通过 Widget 作为入口。

Widget 不可变,意味着页面变化时 Widget 必被重新构建,其固定状态代表一帧静止画面,画面改变时对应 Widget 必变。例如,定义一个 TestWidget,接受 title 和 count 参数显示在 Text 上,如果 count 大于 99 则显示 99。

class TestWidget extends StatelessWidget {
  final String title;
  final int count;

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

  @override
  Widget build(BuildContext context) {
    int displayCount = (count > 99) ? 99 : count;
    return Container(
      child: Text("$title $displayCount"),
    );
  }
}

如果 count 未声明为 final,编译器会警告,因为 Widget 不可变,count 成员不会被保存或二次使用,容易产生歧义。

换成 StatefulWidget,将 build 方法放入 State,State 中的 count 可实现跨帧保存。

class TestWidgetWithState extends StatefulWidget {
  final String title;

  TestWidgetWithState({this.title});

  @override
  _TestWidgetState createState() => _TestWidgetState();
}

class _TestWidgetState extends State<TestWidgetWithState> {
  int count;

  @override
  Widget build(BuildContext context) {
    int displayCount = (count > 99) ? 99 : count;
    return InkWell(
      onTap: () {
        setState(() {
          count++;
        });
      },
      child: Container(
        child: Text("${widget.title} $displayCount"),
      ),
    );
  }
}

关键是理解 Widget 的不可变性,然后知道通过 State 可实现数据跨 Widget 保存和恢复。为什么 State 可以?这涉及 Flutter 中另一个重要知识点:Widget 的背后是什么?

在 Flutter 中,Widget 不是真正控件,而是配置文件,其后的 Element、RenderObject、Layer 等才是实际工作的对象。Element、RenderObject、Layer 是学习理解的核心。

例如,同一个 Text Widget 在页面中多处使用可正常运行渲染,如果是一个真正的 View,不能在一个页面下被多个地方加载使用。在 Flutter 设定中,Widget 是配置文件,告诉 Flutter 如何渲染,它会经过 Element、RenderObject、Layer 最终渲染,因此 Widget 可以是不可变的,每次状态更新都被重构。

回到最初问题:Flutter 的嵌套很恶心?是的,Flutter 设计导致嵌套客观存在,但将 Widget 理解为配置文件,可更好组织代码,例如 Flutter 的 Container 是一个抽象配置模板。参考 Container 可学会 Flutter 代码逻辑的第一步。

由于 Widget 不是真正工作的,嵌套一般不会带来性能问题,因为它不是实际 View,嵌套不会导致严重性能损失。当写了一堆 Widget 加载时,第一次产生对应 Element,Element 持有 Widget 和 RenderObject。画面改变时,Widget 变化更新到 RenderObject,而能跨帧保存的 State 被 Element 持有,用于跨 Widget 保存数据。

因此,Widget 的嵌套一般无性能问题,每个 Widget 状态代表一帧,可理解为配置信息代表当前画面。在 Widget 背后,嵌套的 Padding、Align 等控件最终只是 canvas 的偏移计算。

理解 Widget 控件很重要,Widget 不是真正的 View,只是配置信息。只有理解这点,才能发现 Flutter 的广阔天地,例如:

  • Flutter 控件从 Element 开始是真正工作对象。
  • 要看 Widget 的界面效果,需看对应 RenderObject 的绘制。
  • 要了解不同堆栈或模块页面不干扰,需看 Layer 逻辑。
  • 不是所有 Widget 都有 RenderObject,需理解 Widget、Element、RenderObject、Layer 的对应关系。

这些内容是学习 Flutter 需理解和融会贯通的。了解 Widget 背后的复杂逻辑支撑后,你会发现 Flutter 在实现复杂控件时如此简单,Canvas 组合能力非常强大。具体内容无法在此详述,在相关资料中会深入讲解。

Flutter 是个有坑的框架

最后谈谈 Flutter 的坑,没有框架是完美的。如果框架无问题,我们的竞争力会减弱,可替换性更高。这也是为什么 Android 和 iOS 开发火热后,客户端招聘回归理性,领域成熟后自然“卷”了。

使用框架本身无特殊价值,解决使用框架带来的问题才是特有价值。Flutter 问题不少,例如:

  • WebView 问题:Flutter 特有 UI 机制导致需特殊方式接入 WebView、MapView 等控件,带来性能、键盘、输入框等技术问题。
  • 图片处理和加载:Flutter 在图片处理上能力较弱,单个大图片加载或大量图片列表显示易出现内存和 GPU 溢出问题。处理麻烦,如需借用原生平台,需通过外界纹理实现,维护成本高。
  • 混合开发:Flutter 控件和页面堆栈脱离原生平台,混合开发导致维护成本提高。现有方案如 flutter_boost 和 flutter_thrio 未能很好解决痛点。

然而,在我收到的 Flutter 问题中,大部分与 Flutter 无关,例如:

  • flutter doctor 卡住不动。
  • flutter run 报错。
  • flutter pub get 提示 Dart 版本不对。
  • Gradle 报错显示 timeout。
  • iOS 无法运行到真机。
  • 寻找现成控件。
    这些问题多是查看日志、文档、网络或搜索引擎检索技术的问题。

尽管 Flutter 有各种问题,但综合考虑,它对我现阶段是最合适的 UI 框架。

最后

希望这篇文章能帮助你更全面理解 Flutter,或找到学习方向。引用某位大佬的话:“能大规模商用的技术,都不需要太高的智商,否则这种技术就不可能规模化。某些程序员们,请停止你们的蜜汁自信。”

0 Answers