一、Flutter 简介
Flutter是 Google 于 2015 年 5 月 3 日推出的免费开源跨平台开发框架,可以快速在iOS和Android上构建高质量的原生用户界面。Flutter帮助开发者使用一套代码开发高性能、高稳定性、高帧率、低延迟的Android和iOS应用。Flutter使用的是 Google 自己开发的网络编程语言——Dart 语言。Dart 语言是由Google公司开发的网络编程语言,是面向对象的,类定义的,单继承语言
二、Flutter 框架特性
快速开发
Flutter的热重载能力帮助开发者快捷方便的试验、重构UI、添加特性和修复bug。在仿真器、模拟器、ios、android硬件上体验亚秒级的重载,而不会丢失状态。
跨平台
Flutter 基于图像绘制引擎进行渲染,在不同平台下绘制效果绝对一致,能做到真正的跨平台。
绚丽UI
通过Flutter内建的、漂亮的、有质感设计的Cupertino(ios-flavor)小工具、丰富的动画API,平滑的自然滚动和平台感知,让用户感受UI设计的快乐。
响应式框架
通过Flutter的现代响应式(Reactive)框架、丰富的平台布局、基础组件,能够轻松的构建用户界面。使用强大而灵活的API解决2D、动画、手势、效果等难题。
支持插件,访问原生功能
通过平台api、第三方sdk和原生代码,使应用变得生动。Flutter可以重用现有的java、swift和Objc代码,并在iOS和Android上访问原生特性和SDK。
三、目前各种跨平台方案的对比
开发APP应用,如需要同时兼容 iOS 和 Android 两种平台,有两种技术选择:
1、走原生开发路线,把界面和逻辑在不同平台分别实现;
2、用同一套代码兼容多个平台,但这往往意味着运行速度和产品体验的损失。
除了原生外,目前跨平台技术一般是混合开发,如采用 H5、React Native、Weex、小程序等技术实现跨平台应用。不过这些混合开发,或多或少都能感觉到UI卡顿和体验不流畅,并且开发和学习成本非常高,有各自的局限性。
Flutter 的出现,为开发者提供了一套两全其美的解决方案:既能用原生代码直接调用的方式来加速图形渲染和 UI 绘制,又能同时运行在两大主流移动操作系统上,并且体验和流畅度和原生基本一致、开发效率非常高、学习难度和成本低。
从上面的对比可以看出,Flutter 优势明显:高体验度、高开发效率、低学习成本、高可扩展性。未来 Google Flutter 团队还将会使 Flutter 支持 PC 和 Web 的跨平台开发,实现真正全平台。 针对Flutter的跨平台特性,与react native、weex做对比可以发现,其性能碾压后两者,采用自带Skia绘制引擎,性能堪比原生。
四、 介绍下Flutter的架构理念
由上图可知,Flutter框架自下而上分为Embedder、Engine和Framework三层。其中,Embedder是操作系统适配层,实现了渲染 Surface设置,线程设置,以及平台插件等平台相关特性的适配;Engine层负责图形绘制、文字排版和提供Dart运行时,Engine层具有独立虚拟机,正是由于它的存在,Flutter程序才能运行在不同的平台上,实现跨平台运行;Framework层则是使用Dart编写的一套基础视图库,包含了动画、图形绘制和手势识别等功能,是使用频率最高的一层。
介绍下Flutter的FrameWork层和Engine层,以及它们的作用
Flutter的FrameWork层是用Drat编写的框架(SDK),它实现了一套基础库,包含Material(Android风格UI)和Cupertino(iOS风格)的UI界面,下面是通用的Widgets(组件),之后是一些动画、绘制、渲染、手势库等。这个纯 Dart实现的 SDK被封装为了一个叫作 dart:ui的 Dart库。我们在使用 Flutter写 App的时候,直接导入这个库即可使用组件等功能。
Flutter的Engine层是Skia 2D的绘图引擎库,其前身是一个向量绘图软件,Chrome和 Android均采用 Skia作为绘图引擎。Skia提供了非常友好的 API,并且在图形转换、文字渲染、位图渲染方面都提供了友好、高效的表现。Skia是跨平台的,所以可以被嵌入到 Flutter的 iOS SDK中,而不用去研究 iOS闭源的 Core Graphics / Core Animation。Android自带了 Skia,所以 Flutter Android SDK要比 iOS SDK小很多。
五、Flutter 性能优化
1.重建最小化原则
在调用 setState() 方法重建组件时,一定要最小化重建组件,没有变化的组件不要重建
点击按钮调用setstate, 可以看到每次重建的widget, 需要变化的组件尽量抽取
不变组件建议加上const, 在调用setstate时,也是不会重建
2.避免更改组件树的结构和组件的类型
3.关于Listview优化
ListView是我们最常用的组件之一,用于展示大量数据的列表。如果展示大量数据请使用 ListView.builder 或者 ListView.separated
4.关于 AnimatedBuilder TweenAnimationBuilder 的优化
这里说的是向AnimatedBuilder 、TweenAnimationBuilder 等一类的组件的问题,这些组件都有一个共同点,带有 builder 且其参数重有 child。
以 AnimatedBuilder 为例,如果 builder 中构建的树中包含与动画无关的组件,将这些无关的组件当作 child 传递到 builder 中比直接在 builder 中构建更加有效。
5.谨慎的使用一些组件
六、Flutter是如何做到一套Dart代码可以编译运行在Android和iOS平台的?所以说具体的原理。
Skia绘制,实现跨平台应用层渲染一致性
Method Channel 机制
为了解决调用原生系统底层能力以及相关代码库复用问题,Flutter 为开发者提供了一个轻量级的解决方案,即逻辑层的方法通道(Method Channel)机制。基于方法通道,我们可以将原生代码所拥有的能力,以接口形式暴露给 Dart,从而实现 Dart 代码与原生代码的交互,就像调用了一个普通的 Dart API 一样。
Flutter定义了三种不同类型的Channel
BasicMessageChannel:用于传递字符串和半结构化的信息。
MethodChannel:用于传递方法调用(method invocation)。
EventChannel: 用于数据流(event streams)的通信。
注意
Method Channel 是非线程安全的。原生代码在处理方法调用请求时,如果涉及到异步或非主线程切换,需要确保回调过程是在原生系统的 UI 线程(也就是 Android 和 iOS 的主线程)中执行的,否则应用可能会出现奇怪的 Bug,甚至是 Crash。
七、Flutter 布局
Flutter中拥有30多种预定义的布局widget,常用的有Container、Padding、Center、Flex、Row、Colum、ListView、GridView。按照《Flutter技术入门与实战》上面来说的话,大概分为四类
基础布局组件:Container(容器布局),Center(居中布局),Padding(填充布局),Align(对齐布局),Colum(垂直布局),Row(水平布局),Expanded(配合Colum,Row使用),FittedBox(缩放布局),Stack(堆叠布局),overflowBox(溢出父视图容器)。
宽高尺寸处理:SizedBox(设置具体尺寸),ConstrainedBox(限定最大最小宽高布局),LimitedBox(限定最大宽高布局),AspectRatio(调整宽高比),FractionallySizedBox(百分比布局)
列表和表格处理:ListView(列表),GridView(网格),Table(表格)
其它布局处理:Transform(矩阵转换),Baseline(基准线布局),Offstage(控制是否显示组件),Wrap(按宽高自动换行布局)
八、Flutter 动画类型
1.基于图形动画(以图形的形式制作动画,三方库 Rive 、Lottie)、
2.基于代码动画 (主要针对于 Widgets 的大小、 颜色、形状变动)
在 Flutter 中基于代码动画,也主要分为两种形式:补间(Tween)动画、 拟物动画。
补间(Tween)动画
Tween意为在两者之间,在Tween动画中我们定义开始点和结束点、时间线以及定义转换时间和速度的曲线。然后就交由框架完成如何从开始点过渡到结束点的动画效果。
物理动画
物理动画是对真实世界的行为进行建模,使动画效果类似于现实中的物理效果。例如当你掷球时,它在何处落地,取决于抛球速度有多快、球有多重、距离地面有多远。
Flutter 基础动画和相关类
Animation:Flutter中动画的核心类
AnimationController:动画的管理类
CurvedAnimation:用于定义非线性曲线动画
Tween:补间对象,用于计算动画使用的数据范围之间的插值
Listeners 和StatusListeners:用于监听动画状态的改变
如何创建恰当的动画:
在知道Flutter的动画类型之后,我们需要知道如何创建我们想要的动画效果,在 Flutter 动画也被分为隐式动画、 显示动画。当我们创建一个动画效果时,如果你不知道如何选择这两种方式,你可以试着回答下面的三个问题:
1.动画效果是否"永远"重复?永远是指某个条件下动画一直重复,当条件变动时,动画可以停止。
2.动画的值是否不连续?如下图圆从小到大、从小到大,并未连续的小->大->小->大。
3.动画是否由多个Widgets共同组成?
如果以上三个问题,都是肯定的回答,那么你需要的是显示动画,否则使用隐式动画将会是你更好的选择。
隐式动画
如果你选择了隐式动画之后,首先你可以尝试在 Flutter 框架中寻找符合你要求的 Widgets,Flutter 对隐式动画的命名方式一般为AnimatedFoo, 其中Foo是需要设置动画的属性。如果找不到符合自己需求的内置动画Widget,我们可以使用TweenAnimationBuilder(补间动画生成器)创建自定义的隐式动画。
显示动画
如果你的选择是显示动画,那么你也可以尝试在 Flutter 框架中寻找符合Widgets。Flutter 对显示动画的命名方式一般为FooTransition, 其中Foo是需要设置动画的属性(如SlideTransition)。如果找不到符合自己需求的内置动画Widget。你还有两种选择,创建独立的动画Widget(扩展AnimatedWidget),或者扩展Widget(使用AnimatedBuilder)。
如果在动画执行的过程中,碰到了性能的问题的话,我们可以考虑使用CustomPainter进行动画处理.
九、Flutter 中的状态State
State是一个组件的UI数据模型,是组件渲染时的数据依赖,State状态的生命周期
StatefulWidget.creatWidget:FrameWork会通过调用StatefulWidget.creatWidget来创建第一个State
initState:初始化调用
didChangeDependencies:initState调用结束后调用
build:通过Build 来构建视图,返回一个Widget
deactivate:State被暂时从试图树中移除时,会调用这个函数,暂停状态
dispose:State被永久从试图树中移除时,调用这个方法,销毁状态
didUpdateWidget:当Widget配置发生变化时会调用这个方法,比如热重载,会重新build
setState:当需要更新State试图的时候,重新Build