前言
忙完公司的工作之后,终于能腾出点时间来学习了,除了将基础和源码的学习补回来,还利用闲余的时间玩了一下flutter。刚开始我觉得Flutter的布局嵌套语法很恶心,内心是及其排斥的,但是在学了之后,我只想说:“真香!”
移动开发的现状
随着移动互联网的高速发展,移动App的开发模式也在快速更迭中发展。
最初,为了能够在不同系统环境上运行,通常要求开发团队进行多平台并行开发。通常,开发Android和iOS App一共需要两个开发团队,维护两套源代码,分别进行测试。
后来,开发者们逐渐意识到,这样的开发效率并不高,成本却不低。因此诞生了一个接一个的跨平台解决方案。比如react-native、weex、cordova、ionic等等。但无一例外地,它们都无法摆脱低性能的JavaScript或者原生代码依赖,或多或少地存在不足。
所以急需一个真正能够打通多平台且高性能的框架来“救场”,Flutter则应运而生。
认识 Flutter
Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。作为一种全新的响应式,跨平台,高性能的移动开发框架。从开源以来,已经得到越来越多开发者的喜爱。其中闲鱼、腾讯、美团、字节跳动等大厂都有自己成熟的团队并有深度实践。赶快学起来!
Flutter 特性
那么,Flutter 究竟有哪些特性吸引着这么多开发者的喜爱呢呢?
- 热重载(Hot Reload):有热重载真的太舒服了,可以帮助开发者更高效地进行开发和测试,更利于修复Bug,就这一点比原生安卓制作简直不知道高到哪里去了。
- 统一的应用开发体验:Flutter拥有丰富的库,帮助开发者快速实现项目需求。同时,大部分的工具和库同时支持Android和iOS;
- 界面生动:Flutter支持跨平台开发,同样支持Material Design(原生Android设计语言)和Cupertino(原生iOS设计语言)风格的控件。开发者可根据设计需要实现不同风格的UI界面;
- 原生性能:无论在Android还是iOS环境中,Flutter可以提供与原生应用一样的性能,甚至支持120 HZ的高刷新率;
- 响应式框架:Flutter支持响应式框架,在某些场景下,开发者无需付出任何代价,即可完成不同屏幕的适配,使UI的构建更加轻松;
- 混合开发:Flutter可以与平台原生代码相结合,支持较新的Kotlin和Swift开发语言。借助该特性,可以轻松访问Android或iOS上的原生系统功能和系统API。
Flutter 核心思想
一切都是控件(Widget)(Everything’s a Widget)
在Flutter的世界里,包括views,view controllers,layouts等在内的概念都建立在Widget之上。widget是flutter功能的抽象描述。
也就是说,在Flutter中,一个应用就是有许许多多的Widget组合而成的。
Flutter 分层架构
从flutter的架构图中不难看出widget是整个视图描述的基础。
Flutter Framework
这是一个纯 Dart实现的 SDK,它实现了一套基础库,自底向上,我们来简单介绍一下:
底下两层(Foundation和Animation、Painting、Gestures)在Google的一些视频中被合并为一个dart UI层,对应的是Flutter中的dart:ui包,它是Flutter引擎暴露的底层UI库,提供动画、手势及绘制能力。
Rendering层,这一层是一个抽象的布局层,它依赖于dart UI层,Rendering层会构建一个UI树,当UI树有变化时,会计算出有变化的部分,然后更新UI树,最终将UI树绘制到屏幕上,这个过程类似于React中的虚拟DOM。Rendering层可以说是Flutter UI框架最核心的部分,它除了确定每个UI元素的位置、大小之外还要进行坐标变换、绘制(调用底层dart:ui)。
Widgets层是Flutter提供的的一套基础组件库,在基础组件库之上,Flutter还提供了 Material 和Cupertino两种视觉风格的组件库。而我们Flutter开发的大多数场景,只是和这两层打交道。
Flutter Engine
这是一个纯 C++实现的 SDK,其中包括了 Skia引擎、Dart运行时、文字排版引擎等。在代码调用 dart:ui库时,调用最终会走到Engine层,然后实现真正的绘制逻辑。
聊完架构接下来我们聊一聊flutter的生命周期。
Flutter 生命周期
Flutter的生命周期主要包括两大部分:state和App。
state 生命周期
widget是immutable的,发生变化的时候需要重建,所以谈不上状态。StatefulWidget 中的状态保持其实是通过State类来实现的。State拥有一套自己的生命周期:
名称 | 状态 |
---|---|
initState | 插入渲染树时调用,只调用一次 |
didChangeDependencies | state依赖的对象发生变化时调用 |
didUpdateWidget | 组件状态改变时候调用,可能会调用多次 |
build | 构建Widget时调用 |
deactivate | 当移除渲染树的时候调用 |
dispose | 组件即将销毁时调用 |
生命周期状态图如下:
注意:
didChangeDependencies有两种情况会被调用。
- 创建时候在initState 之后被调用
- 在依赖的InheritedWidget发生变化的时候会被调用
正常的退出流程中会执行deactivate然后执行dispose。但是也会出现deactivate以后不执行dispose,直接加入树中的另一个节点的情况。
这里的状态改变包括两种可能:1.通过setState内容改变 2.父节点的state状态改变,导致孩子节点的同步变化。
App生命周期
如果想要知道App的生命周期,那么需要通过WidgetsBindingObserver的didChangeAppLifecycleState 来获取。通过该接口可以获取是生命周期在AppLifecycleState类中。常用状态包含如下几个:
名称 | 状态 |
---|---|
resumed | 可见并能相应用户的输入 |
inactive | 处在并不活动状态,无法处理用户相应 |
paused | 不可见并不能相应用户的输入,但是在后台继续活动中 |
一个实际场景中的例子:
在不考虑suspending的情况下:从后台切入前台生命周期变化如下:
AppLifecycleState.inactive -> AppLifecycleState.resumed;
从前台压后台生命周期变化如下:
AppLifecycleState.inactive -> AppLifecycleState.paused;
Flutter 环境搭建
工欲善其事必先利其器,环境搭建可以参考 Flutter实战 一步一步来搭建。
Flutter 实践
经过一个多礼拜的折腾,自己也尝试写了几个小demo: