GSYFlutterBook/Flutter-msjj.md

410 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

谷歌大会之后,有不少人咨询了我 **Flutter** 相关的问题,其中有不少是和面试相关的,如今一些招聘上也开始罗列 **Flutter** 相关要求,最后想了想还是写一期总结吧,也算是 **Flutter** 的阶段复习。
```
⚠️系统完整的学习是必须需要的,这里只能帮你总结一些知识点,更多的还请查阅 Dart/Flutter 官网。
```
本篇主要是知识点总结,如有疑问可点击各文章链接了解详情,或者查阅我 [掘金专栏](https://juejin.im/user/582aca2ba22b9d006b59ae68/posts)。
## Dart 部分
其实学习过 `JavaScript` 或者 `Java/Kotlin` 的人,在学习 `Dart` 上几乎是没什么难度的,**Dart 综合了动态语言和静态语言的特性,** 这里主要提供一些不一样,或者有意思的概念。
- 1、`Dart` 属于是**强类型语言** ,但可以用 `var` 来声明变量,`Dart` 会**自推导出数据类型**`var` 实际上是编译期的“语法糖”。**`dynamic` 表示动态类型** 被编译后,实际是一个 `object` 类型,在编译期间不进行任何的类型检查,而是在运行期进行类型检查。
- 2、`Dart` 中 `if` 等语句只支持 `bool` 类型,`switch` 支持 String 类型。
- 3、`Dart` 中**数组和 `List` 是一样的。**
- 4、`Dart` 中,**`Runes` 代表符号文字** , 是 UTF-32 编码的字符串, 用于如 ` Runes input = new Runes('\u{1f596} \u{1f44d}');`
- 5、**`Dart` 支持闭包。**
- 6、`Dart` 中 number 类型分为 **int 和 double ,没有 float 类型。**
- 7、`Dart` 中 **级联操作符** 可以方便配置逻辑,如下代码:
```
event
..id = 1
..type = ""
..actor = "";
```
- 8、赋值操作符
比较有意思的赋值操作符有:
```
AA ?? "999" ///表示如果 AA 为空返回999
AA ??= "999" ///表示如果 AA 为空,给 AA 设置成 999
AA ~/999 ///AA 对于 999 整除
```
- 9、可选方法参数
`Dart` 方法可以设置 **参数默认值****指定名称**
比如: `getDetail(Sting userName, reposName, {branch = "master"}){}` 方法,这里 branch 不设置的话,默认是 “master” 。参数类型 可以指定或者不指定。调用效果: `getRepositoryDetailDao(“aaa", "bbbb", branch: "dev");`
- 10、作用域
**`Dart` 没有关键词 `public` 、`private` 等修饰符,`_` 下横向直接代表 `private` ,但是有 `@protected` 注解 。**
- 11、构造方法
`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);
}
```
- 12、getter setter 重写
`Dart` 中所有的基础类型、类等都继承 `Object` ,默认值是 `NULL` 自带 `getter``setter` ,而如果是 `final` 或者 `const` 的话,那么它只有一个 `getter` 方法,`Object` 都支持 getter、setter 重写:
```
@override
Size get preferredSize {
return Size.fromHeight(kTabHeight + indicatorWeight);
}
```
- 13、Assert(断言)
`assert` 只在检查模式有效,在开发过程中,` assert(unicorn == null);` 只有条件为真才正常,否则直接抛出异常,一般用在开发过程中,某些地方不应该出现什么状态的判断。
- 14、重写运算符如下所示重载 `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));
}
```
支持重载的操作符
![](http://img.cdn.guoshuyu.cn/20190604_Flutter-msjj/image1)
- 类、接口、继承
`Dart` 中没有接口,类都可以作为接口,把某个类当做接口实现时,只需要使用 `implements` ,然后复写父类方法即可。
`Dart` 中支持 `mixins` ,按照出现顺序应该为` extends` 、 `mixins` 、`implements` 。
- ### Zone
`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完整开发实战详解(十一、全面深入理解Stream)》](https://juejin.im/post/5cc2acf86fb9a0321f042041)
- ### 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](https://dart.dev/guides/language/language-tour#generators)
>
> [code_generator.dart](https://github.com/dart-lang/sdk/blob/master/pkg/dev_compiler/lib/src/analyzer/code_generator.dart)
>
> [Flutter完整开发实战详解(十一、全面深入理解Stream)](https://juejin.im/post/5cc2acf86fb9a0321f042041)
- ### 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完整开发实战详解(十一、全面深入理解Stream)》](https://juejin.im/post/5cc2acf86fb9a0321f042041)
## Flutter 部分
Flutter 和 React Native 不同主要在于 **Flutter UI是直接通过 skia 渲染的 ,而 React Native 是将 js 中的控件转化为原生控件,通过原生去渲染的** ,相关更多可查看:[《移动端跨平台开发的深度解析》](https://juejin.im/post/5b395eb96fb9a00e556123ef)。
- Flutter 中存在 `Widget``Element` 、`RenderObject` 、`Layer` 四棵树,其中 **`Widget``Element` 是一对多的关系**
- `Element` 中持有`Widget` 和 `RenderObject` **`Element``RenderObject` 是一一对应的关系(除去 `Element` 不存在 `RenderObject` 的情况,如 `ComponentElement` 是不具备 `RenderObject`)**
-`RenderObject``isRepaintBoundary``true` 时,那么个区域形成一个 `Layer`,所以**不是每个 `RenderObject` 都具有 `Layer` 的,因为这受 `isRepaintBoundary` 的影响。**
> 更多相关可查阅 [《Flutter完整开发实战详解(九、 深入绘制原理)》](https://juejin.im/post/5ca0e0aff265da309728659a)
- Flutter 中 `Widget` 不可变,每次保持在一帧,如果**发生改变是通过 `State` 实现跨帧状态保存**,而**真实完成布局和绘制数组的是 `RenderObject` ** `Element` 充当两者的桥梁, **`State` 就是保存在 `Element` 中。**
- **Flutter 中的 `BuildContext` 只是接口,而 `Element` 实现了它。**
- Flutter 中 **`setState` 其实是调用了 `markNeedsBuild`** ,该方法内部**标记此`Element ` 为 `Dirty`** ,然后在下一帧 `WidgetsBinding.drawFrame` 才会被绘制,这可以看出 **`setState` 并不是立即生效的。**
- Flutter 中 `RenderObject``attch`/`layout` 之后会通过 `markNeedsPaint();` 使得页面重绘,流程大概如下:
![](http://img.cdn.guoshuyu.cn/20190604_Flutter-msjj/image2)
**通过isRepaintBoundary 往上确定了更新区域,通过 requestVisualUpdate 方法触发更新往下绘制。**
- 正常情况 `RenderObject ` 的布局相关方法调用顺序是 **`layout` -> `performResize` -> `performLayout` -> `markNeedsPaint` ,** 但是用户一般不会直接调用 `layout`,而是**通过 `markNeedsLayout`** ,具体流程如下:
![](http://img.cdn.guoshuyu.cn/20190604_Flutter-msjj/image3)
- 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` 状态发生变化时,会调用。
![](http://img.cdn.guoshuyu.cn/20190604_Flutter-msjj/image4)
------
- 通过 `StreamBuilder``FutureBuilder` 我们可以快速使用 `Stream``Future` 快速构建我们的异步控件: [《Flutter完整开发实战详解(十一、全面深入理解Stream)》](https://juejin.im/post/5cc2acf86fb9a0321f042041)
- 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完整开发实战详解(十三、全面深入触摸和滑动原理)》](https://juejin.im/post/5cd54839f265da03b2044c32)
- Flutter 中 `ListView` 滑动其实都是通过改变 `ViewPort` 中的 `child` 布局来实现显示的。
- 常用状态管理的:目前有 `scope_model` 、` flutter_redux` 、`fish_redux` 、`bloc + Stream` 等几种模式,具体可见 : [《Flutter完整开发实战详解(十二、全面深入理解状态管理设计)》](https://juejin.im/post/5cc816866fb9a03231209c7c)
### Platform Channel
Flutter 中可以通过 `Platform Channel` 让 Dart 代码和原生代码通信的:
> - `BasicMessageChannel` :用于传递字符串和半结构化的信息。
> - `MethodChannel` 用于传递方法调用method invocation
> - `EventChanne` l: 用于数据流event streams的通信。
**同时 `Platform Channel` 并非是线程安全的** ,更多详细可查阅闲鱼技术的 [《深入理解Flutter Platform Channel》](https://www.jianshu.com/p/39575a90e820)
其中基础数据类型映射如下:
![](http://img.cdn.guoshuyu.cn/20190604_Flutter-msjj/image5)
-------
### 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` 中移除闪屏。
> 好了,暂时都这里了,有问题修改会或则补充的,后面再加上。
### 资源推荐
* Github [https://github.com/CarGuo/](https://github.com/CarGuo)
* **开源 Flutter 完整项目https://github.com/CarGuo/GSYGithubAppFlutter**
* **开源 Flutter 多案例学习型项目: https://github.com/CarGuo/GSYFlutterDemo**
* **开源 Fluttre 实战电子书项目https://github.com/CarGuo/GSYFlutterBook**
##### 完整开源项目推荐:
* [GSYGithubApp Flutter](https://github.com/CarGuo/GSYGithubAppFlutter )
* [GSYGithubApp React Native](https://github.com/CarGuo/GSYGithubApp )
* [GSYGithubAppWeex](https://github.com/CarGuo/GSYGithubAppWeex)
##### 文章
[《Flutter完整开发实战详解(一、Dart语言和Flutter基础)》](https://juejin.im/post/5b631d326fb9a04fce524db2)
[《Flutter完整开发实战详解(二、 快速开发实战篇)》](https://juejin.im/post/5b685a2a5188251ac22b71c0)
[《Flutter完整开发实战详解(三、 打包与填坑篇)》](https://juejin.im/post/5b6fd4dc6fb9a0099e711162)
[《Flutter完整开发实战详解(四、Redux、主题、国际化)》](https://juejin.im/post/5b79767ff265da435450a873)
[《Flutter完整开发实战详解(五、 深入探索)》](https://juejin.im/post/5bc450dff265da0a951f032b)
[《Flutter完整开发实战详解(六、 深入Widget原理)》](https://juejin.im/post/5c7e853151882549664b0543)
[《Flutter完整开发实战详解(七、 深入布局原理)》](https://juejin.im/post/5c8c6ef7e51d450ba7233f51)
[《Flutter完整开发实战详解(八、 实用技巧与填坑)》](https://juejin.im/post/5c9e328251882567b91e1cfb)
[《Flutter完整开发实战详解(九、 深入绘制原理)》](https://juejin.im/post/5ca0e0aff265da309728659a)
[《Flutter完整开发实战详解(十、 深入图片加载流程)》](https://juejin.im/post/5cb1896ce51d456e63760449)
[《Flutter完整开发实战详解(十一、全面深入理解Stream)》](https://juejin.im/post/5cc2acf86fb9a0321f042041)
[《Flutter完整开发实战详解(十二、全面深入理解状态管理设计)》](https://juejin.im/post/5cc816866fb9a03231209c7c)
[《Flutter完整开发实战详解(十三、全面深入触摸和滑动原理)》](https://juejin.im/post/5cd54839f265da03b2044c32)
[《跨平台项目开源项目推荐》](https://juejin.im/post/5b6064a0f265da0f8b2fc89d)
[《移动端跨平台开发的深度解析》](https://juejin.im/post/5b395eb96fb9a00e556123ef)
![我们还会再见吗?](http://img.cdn.guoshuyu.cn/20190604_Flutter-msjj/image6)