转载:恋猫de小郭(掘金)
谷歌大会以后,有很多人咨询了我 Flutter 相关的问题,其中有很多是和面试相关的,现在一些招聘上也开始罗列 Flutter 相关要求,最后想了想仍是写一期总结吧,也算是 Flutter 的阶段复习。java
⚠️系统完整的学习是必须须要的,这里只能帮你总结一些知识点,更多的还请查阅 Dart/Flutter 官网。
本篇主要是知识点总结,若有疑问可点击各文章连接了解详情,或者查阅我 掘金专栏。android
https://juejin.im/user/582aca...git
其实学习过 JavaScript 或者 Java/Kotlin 的人,在学习 Dart 上几乎是没什么难度的,Dart 综合了动态语言和静态语言的特性, 这里主要提供github
一些不同,或者有意思的概念面试
一、Dart 属因而强类型语言 ,但能够用 var 来声明变量,Dart 会自推导出数据类型,var 其实是编译期的“语法糖”。dynamic 表示动态类型, 被编译后,实际是一个 object 类型,在编译期间不进行任何的类型检查,而是在运行期进行类型检查。json
二、Dart 中 if 等语句只支持 bool 类型,switch 支持 String 类型。redux
三、Dart 中数组和 List 是同样的。数组
四、Dart 中,Runes 表明符号文字 , 是 UTF-32 编码的字符串, 用于如 Runes input = new Runes('🖖 👍');安全
五、Dart 支持闭包。闭包
六、Dart 中 number 类型分为 int 和 double ,没有 float 类型。
七、Dart 中 级联操做符 能够方便配置逻辑,以下代码:
event ..id = 1 ..type = "" ..actor = "";
八、赋值操做符
比较有意思的赋值操做符有:
AA ?? "999" ///表示若是 AA 为空,返回999 AA ??= "999" ///表示若是 AA 为空,给 AA 设置成 999 AA ~/999 ///AA 对于 999 整除
九、可选方法参数
Dart 方法能够设置 参数默认值 和 指定名称 。
好比:getDetail(Sting userName, reposName, {branch = "master"}){} 方法,这里 branch 不设置的话,默认是 “master” 。参数类型 能够指定或者不指定。
调用效果:
getRepositoryDetailDao(“aaa", "bbbb", branch: "dev"); 。
十、做用域
Dart 没有关键词 public 、private 等修饰符,_ 下横向直接表明 private ,可是有 @protected 注解 。
十一、构造方法
Dart 中的多构造方法,能够经过命名方法实现。
默认构造方法只能有一个,而经过 Model.empty() 方法能够建立一个空参数的类,其实方法名称随你喜欢,而变量初始化值时,只须要经过 this.name 在构造方法中指定便可:
class ModelA { String name; String tag; //默认构造方法,赋值给name和tag ModelA(this.name, this.tag); //返回一个空的ModelA ModelA.empty(); //返回一个设置了name的ModelA ModelA.forName(this.name); }
十二、getter setter 重写
Dart 中全部的基础类型、类等都继承 Object ,默认值是 NULL, 自带 getter 和 setter ,而若是是 final 或者 const 的话,那么它只有一个 getter 方法,Object 都支持 getter、setter 重写:
@override Size get preferredSize { return Size.fromHeight(kTabHeight + indicatorWeight); }
1三、Assert(断言)
assert 只在检查模式有效,在开发过程当中,assert(unicorn == null); 只有条件为真才正常,不然直接抛出异常,通常用在开发过程当中,某些地方不该该出现什么状态的判断。
1四、重写运算符,以下所示重载 operator 后对类进行 +/- 操做。
class Vector { final int x, y; Vector(this.x, this.y); Vector operator +(Vector v) => Vector(x + v.x, y + v.y); Vector operator -(Vector v) => Vector(x - v.x, y - v.y); ··· } void main() { final v = Vector(2, 3); final w = Vector(2, 2); assert(v + w == Vector(4, 5)); assert(v - w == Vector(0, 1)); }
支持重载的操做符 :
15. 类、接口、继承
Dart 中没有接口,类均可以做为接口,把某个类当作接口实现时,只须要使用 implements ,而后复写父类方法便可。
Dart 中支持 mixins ,按照出现顺序应该为extends 、 mixins 、implements 。
Dart 中可经过 Zone 表示指定代码执行的环境,相似一个沙盒概念,在 Flutter 中 C++ 运行 Dart 也是在 _runMainZoned 内执行 runZoned 方法启动,而咱们也能够经过 Zone ,在运行环境内捕获全局异常等信息:
runZoned(() { runApp(FlutterReduxApp()); }, onError: (Object obj, StackTrace stack) { print(obj); print(stack); });
同时你能够给 runZoned 注册方法,在须要时执行回调,以下代码所示,这样的在一个 Zone 内任何地方,只要能获取 onData 这个 ZoneUnaryCallback,就均可以调用到 handleData
///最终须要处理的地方 handleData(result) { print("VVVVVVVVVVVVVVVVVVVVVVVVVVV"); print(result); } ///返回获得一个 ZoneUnaryCallback var onData = Zone.current.registerUnaryCallback<dynamic, int>(handleData); ///执行 ZoneUnaryCallback 返回数据 Zone.current.runUnary(onData, 2);
异步逻辑能够经过 scheduleMicrotask 能够插入异步执行方法:
Zone.current.scheduleMicrotask((){ //todo something });
更多可参看 :
《Flutter完整开发实战详解(11、全面深刻理解Stream)》
https://juejin.im/post/5cc2ac...
Future
Future 简单了说就是对 Zone 的封装使用。
好比 Future.microtask 中主要是执行了 Zone 的 scheduleMicrotask ,而 result._complete 最后调用的是 _zone.runUnary 等等。
factory Future.microtask(FutureOr<T> computation()) { _Future<T> result = new _Future<T>(); scheduleMicrotask(() { try { result._complete(computation()); } catch (e, s) { _completeWithErrorCallback(result, e, s); } }); return result; }
Dart 中可经过 async/await 或者 Future 定义异步操做,而事实上 async/await 也只是语法糖,最终仍是经过编译器转为 Future。
有兴趣看这里 :generators
code_generator.dart
Flutter完整开发实战详解(11、全面深刻理解Stream)
Stream 也是有对Zone 的另一种封装使用。
Dart 中另一种异步操做, async / yield 或者 Stream 可定义 Stream 异步, async / yield 也只是语法糖,最终仍是经过编译器转为 Stream。
Stream 还支持同步操做。
1)、Stream 中主要有 Stream 、 StreamController 、StreamSink 和 StreamSubscription 四个关键对象,大体能够总结为:
StreamController :如类名描述,用于整个 Stream 过程的控制,提供各种接口用于建立各类事件流。
StreamSink :通常做为事件的入口,提供如 add , addStream 等。
Stream :事件源自己,通常可用于监听事件或者对事件进行转换,如 listen 、where 。
StreamSubscription :事件订阅后的对象,表面上用于管理订阅过等各种操做,如 cacenl 、pause ,同时在内部也是事件的中转关键。
2)、通常经过 StreamController 建立 Stream;经过 StreamSink 添加事件;经过 Stream 监听事件;经过 StreamSubscription 管理订阅。
3)、Stream 中支持各类变化,好比map 、expand 、where 、take 等操做,同时支持转换为 Future 。
更多可参看 :
《Flutter完整开发实战详解(11、全面深刻理解Stream)》
https://juejin.im/post/5cc2ac...
Flutter 和 React Native 不一样主要在于 Flutter UI是直接经过 skia 渲染的 ,而 React Native 是将 js 中的控件转化为原生控件,经过原生去渲染的 ,相关更多可查看:
《移动端跨平台开发的深度解析》
https://juejin.im/post/5b395e...
Flutter 中存在 Widget 、 Element 、RenderObject 、Layer 四棵树,其中Widget 与 Element 是多对一的关系
Element 中持有Widget 和 RenderObject , 而 Element 与 RenderObject 是一一对应的关系 ,
当 RenderObject 的 isRepaintBoundary 为 true 时,那么个区域造成一个 Layer,因此不是每一个 RenderObject 都具备 Layer 的,由于这受 isRepaintBoundary 的影响。
更多相关可查阅
《Flutter完整开发实战详解(9、 深刻绘制原理)》
https://juejin.im/post/5ca0e0...
Flutter 中 Widget 不可变,每次保持在一帧,若是发生改变是经过 State 实现跨帧状态保存,而真实完成布局和绘制数组的是 RenderObject , Element 充当二者的桥梁, State 就是保存在 Element 中。
Flutter 中的 BuildContext 只是接口,而 Element 实现了它。
Flutter 中 setState 实际上是调用了 markNeedsBuild ,该方法内部标记此Element 为 Dirty ,而后在下一帧 WidgetsBinding.drawFrame 才会被绘制,这能够看出 setState 并非当即生效的。
Flutter 中 RenderObject 在 attch/layout 以后会经过 markNeedsPaint(); 使得页面重绘,流程大概以下:
经过isRepaintBoundary 往上肯定了更新区域,经过 requestVisualUpdate 方法触发更新往下绘制。
正常状况 RenderObject 的布局相关方法调用顺序是 :layout -> performResize -> performLayout -> markNeedsPaint , 可是用户通常不会直接调用 layout,而是经过 markNeedsLayout ,具体流程以下:
Flutter 中通常 json 数据从 String 转为 Object 的过程当中都须要先通过 Map 类型。
Flutter 中 InheritedWidget 通常用于状态共享,如Theme 、Localizations 、 MediaQuery 等,都是经过它实现共享状态,这样咱们能够经过 context 去获取共享的状态,好比 ThemeData theme = Theme.of(context);
在 Element 的 inheritFromWidgetOfExactType 方法实现里,有一个 Map<Type, InheritedElement> _inheritedWidgets 的对象。_inheritedWidgets 通常状况下是空的,只有当父控件是 InheritedWidget 或者自己是 InheritedWidgets 时才会有被初始化,而当父控件是 InheritedWidget 时,这个 Map 会被一级一级往下传递与合并 。
因此当咱们经过 context 调用 inheritFromWidgetOfExactType 时,就能够往上查找到父控件的 Widget 。
Flutter 中默认主要经过 runtimeType 和 key 判断更新:
static bool canUpdate(Widget oldWidget, Widget newWidget) { return oldWidget.runtimeType == newWidget.runtimeType && oldWidget.key == newWidget.key; } }
initState() 表示当前 State 将和一个 BuildContext 产生关联,可是此时BuildContext 没有彻底装载完成,若是你须要在该方法中获取 BuildContext ,能够 new Future.delayed(const Duration(seconds: 0, (){//context}); 一下。
didChangeDependencies() 在 initState() 以后调用,当 State 对象的依赖关系发生变化时,该方法被调用,初始化时也会调用。
deactivate() 当 State 被暂时从视图树中移除时,会调用这个方法,同时页面切换时,也会调用。
dispose() Widget 销毁了,在调用这个方法以前,总会先调用 deactivate()。
didUpdateWidge 当 widget 状态发生变化时,会调用。
经过 StreamBuilder 和 FutureBuilder 咱们能够快速使用 Stream 和 Future 快速构建咱们的异步控件:
《Flutter完整开发实战详解(11、全面深刻理解Stream)》
https://juejin.im/post/5cc2ac...
Flutter 中 runApp 启动入口实际上是一个 WidgetsFlutterBinding ,它主要是经过 BindingBase 的子类 GestureBinding 、ServicesBinding 、 SchedulerBinding 、PaintingBinding 、SemanticsBinding 、 RendererBinding 、WidgetsBinding 等,经过 mixins 的组合而成的。
Flutter 中的 Dart 的线程是以事件循环和消息队列的形式存在,包含两个任务队列,一个是 microtask 内部队列,一个是 event 外部队列,而 microtask 的优先级又高于 event 。
由于 microtask 的优先级又高于 event, 同时会阻塞event 队列,因此若是 microtask 太多就可能会对触摸、绘制等外部事件形成阻塞卡顿哦。
Flutter 中存在四大线程,分别为 UI Runner、GPU Runner、IO Runner, Platform Runner (原生主线程) ,同时在 Flutter 中能够经过 isolate 或者 compute 执行真正的跨线程异步操做。
Flutter 中经过 PlatformView 能够嵌套原生 View 到 Flutter UI 中,这里面实际上是使用了 Presentation + VirtualDisplay + Surface 等实现的,大体原理就是:
使用了相似副屏显示的技术,VirtualDisplay 类表明一个虚拟显示器,调用 DisplayManager 的 createVirtualDisplay() 方法,将虚拟显示器的内容渲染在一个 Surface 控件上,而后将 Surface 的 id 通知给 Dart,让 engine 绘制时,在内存中找到对应的 Surface 画面内存数据,而后绘制出来。em... 实时控件截图渲染显示技术。
Flutter 的 Debug 下是 JIT 模式,release下是AOT模式。
Flutter 中能够经过 mixins AutomaticKeepAliveClientMixin ,而后重写 wantKeepAlive 保持住页面,记得在被保持住的页面 build 中调用 super.build 。(由于 mixins 特性)。
Flutter 手势事件主要是经过竞技判断的:
主要有 hitTest 把全部须要处理的控件对应的 RenderObject , 从 child 到 parent 所有组合成列表,从最里面一直添加到最外层。
而后从队列头的 child 开始 for 循环执行 handleEvent 方法,执行 handleEvent 的过程不会被拦截打断。
通常状况下 Down 事件不会决出胜利者,大部分时候是在 MOVE 或者 UP 的时候才会决出胜利者。
竞技场关闭时只有一个的就直接胜出响应,没有胜利者就拿排在队列第一个强制胜利响应。
同时还有 didExceedDeadline 处理按住时的 Down 事件额外处理,同时手势处理通常在 GestureRecognizer 的子类进行。
更多详细请查看:
《Flutter完整开发实战详解(十3、全面深刻触摸和滑动原理)》
https://juejin.im/post/5cd548...
Flutter 中 ListView 滑动其实都是经过改变 ViewPort 中的 child 布局来实现显示的。
经常使用状态管理的:目前有 scope_model 、flutter_redux 、fish_redux 、bloc + Stream 等几种模式,具体可见 :
《Flutter完整开发实战详解(12、全面深刻理解状态管理设计)》
https://juejin.im/post/5cc816...
Flutter 中能够经过 Platform Channel 让 Dart 代码和原生代码通讯的:
- BasicMessageChannel :用于传递字符串和半结构化的信息。
- MethodChannel :用于传递方法调用(method invocation)。
- EventChanne l: 用于数据流(event streams)的通讯。
同时 Platform Channel 并不是是线程安全的 ,更多详细可查阅闲鱼技术的
《深刻理解Flutter Platform Channel》
https://www.jianshu.com/p/395...
其中基础数据类型映射以下:
Android 中 Flutter 默认启动时会在 FlutterActivityDelegate.java 中读取 AndroidManifset.xml 内 meta-data 标签,其中 io.flutter.app.android.SplashScreenUntilFirstFrame 标志位若是为 ture ,就会启动 Splash 画面效果(相似IOS的启动页面)。
启动时原生代码会读取 android.R.attr.windowBackground 获得指定的 Drawable , 用于显示启动闪屏效果,以后而且经过 flutterView.addFirstFrameListener,在onFirstFrame 中移除闪屏。
好了,暂时都这里了,有问题修改会或则补充的,后面再加上。
Github :
本文Demo :
https://github.com/CarGuo/sta...
本文代码 :
https://github.com/CarGuo/GSY...
GSYGithubApp Flutter
https://github.com/CarGuo/GSY...
GSYGithubApp React Native
https://github.com/CarGuo/sta...
GSYGithubAppWeex
https://github.com/CarGuo/GSY...