前言
近期有不少人咨询了我 Flutter 相关的问题,其中有不少是和面试相关的,如今一些招聘上也开始罗列 Flutter 相关要求,于是想了想还是写一期总结吧,也算是 Flutter 学习的复习。
系统完整的学习是必须需要的,这里只能帮你总结一些知识点,更多的还请查阅 Flutter 官网。喜欢的小伙伴欢迎关注,我会定期分享Android知识点及解析,还会不断更新的BATJ面试专题,欢迎大家前来探讨交流,如有好的文章也欢迎投稿。
Flutter 部分
基础知识
Flutter 和 React Native 不同主要在于 Flutter UI是直接通过 skia 渲染的 ,而 React Native 是将 js 中的控件转化为原生控件,通过原生去渲染的 。
-
Flutter 中存在
Widget、Element、RenderObject、Layer四棵树,其中Widget与Element是一对多的关系 -
Element中持有Widget和RenderObject, 而Element与RenderObject是一一对应的关系 -
当
RenderObject的isRepaintBoundary为true时,那么个区域形成一个Layer,所以不是每个RenderObject都具有Layer的,因为这受isRepaintBoundary的影响。 -
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;
}
}
Flutter 中的生命周期
-
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 中
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执行真正的跨线程异步操作。
PlatformView
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 中
ListView滑动其实都是通过改变ViewPort中的child布局来实现显示的。 -
常用状态管理的:目前有
scope_model、flutter_redux、fish_redux、bloc + Stream等几种模式。
Platform Channel
Flutter 中可以通过 Platform Channel 让 Dart 代码和原生代码通信的:
BasicMessageChannel:用于传递字符串和半结构化的信息。MethodChannel:用于传递方法调用(method invocation)。EventChannel: 用于数据流(event streams)的通信。
同时 Platform Channel 并非是线程安全的 。
其中基础数据类型映射如下:
Android 启动页
Android 中 Flutter 默认启动时会在 FlutterActivityDelegate.java 中读取 AndroidManifset.xml 内 meta-data 标签,其中 io.flutter.app.android.SplashScreenUntilFirstFrame 标志位如果为 ture ,就会启动 Splash 画面效果(类似IOS的启动页面)。
启动时原生代码会读取 android.R.attr.windowBackground 得到指定的 Drawable , 用于显示启动闪屏效果,之后并且通过 flutterView.addFirstFrameListener,在onFirstFrame 中移除闪屏。
觉得文章不错的小伙伴帮忙点点赞加关注哦 ,有什么问题的话也欢迎大家前来探讨交流。












网友评论