0、Dart是值传递还是引用传递?
Dart是值传递。每次调用函数,传递过去的都是对象的内存地址,不是对象的复制。
知识点扩展:Swift的struct是值类型,class是引用类型;意思是:声明一个新的变量指向这个结构体,改变某个属性,原本的结构体属性不会发生变化;而类会随着改变
1、Flutter的核心树
Widget:
1、使用配置和状态,描述view的样子
2、当一个Widget发生改变时,Widget会重新build它的描述
Element:
1、Element是widget的实例,在树中详细的位置。
RenderObject:
1、渲染树上的一个对象。
2、RenderObject是渲染库的核心。
2、flutter 中Widget的分类
1、组合类
statelesswidget、statefullwidget
2、代理类
inheritedwidget:用于状态共享,Theme 、Localizations 、 MediaQuery 等,都是通过它进行状态共享,通过context获取共享的状态,ThemeData theme = Theme.of(context);
ParentDataWidget
3、绘制类RenderObjectWidget
RenderObject 的布局相关方法调用顺序是 : layout -> performResize -> performLayout -> markNeedsPaint
3、mixin extends implement 之间的关系?
继承extends 混入mixin 接口实现implement,三者可以同时存在,前后顺序是extends,mixin,implement。
Flutter的继承是单继承,子类重写超类的方法用override,子类调用超类的方法用super。
mixin是为了解决继承方面的问题而引入的机制,Dart为了支持多重继承,引入了mixin关键字。mixins的对象是类,mixins绝不是继承,也不是接口,而是一种全新的特性,可以mixins多个类,mixins的使用需要满足一定条件。
mixin的使用条件:
1、mixin类只能继承自object
2、mixin类不能有构造函数
3、一个类可以mixins多个mixins类,但是不破坏flutter的单继承
mixin就是为了解决Dart的多继承问题,但是mixin不能有构造方法,避免继承多个类产生构造方法冲突
4、Dart的语言特性
1、Dart一切都是对象,所有的对象继承自Object。
2、Dart和Swift一样都是强类型语言,可以用var或 dynamic来声明一个变量,Dart会自动推断其数据类型。
3、没有赋值初始值的变量都会默认null
4、Dart支持顶层方法,如main方法,可以在方法内部创建方法
5、Dart支持顶层变量,也支持类变量或对象变量
6、Dart没有public protected private等关键字,如果变量以下划线开头(_button),表示这个变量在库中是私有的。
5、Dart 中的级联操作符 (未使用疑问)
Dart 当中的 「..」意思是 「级联操作符」,为了方便配置而使用。「..」和「.」不同的是 调用「..」后返回的相当于是 this,而「.」返回的则是该方法返回的值 。
6、Dart 的单线程模型是如何运行的?
Dart在单线程中是以消息循环机制来运行的,包含两个任务队列,微任务队列(microtask queue)、事件队列(event queue)。
flutter启动后,消息循环就启动了。按照先进先出原则逐个执行微任务队列中的任务,执行完毕后执行事件队列中的任务,事件队列中的任务执行完毕之后,在执行微任务队列,如此循环往复。
7、await for 与 stream流 (待深入研究)
Stream<String> stream = new Stream<String>.fromIterable(['1', '2', '3', '4']);
main() async{
print('start');
await for(String s in stream){
print(s);
}
print('end..');
}
结果
start
1
2
3
4
end..
await for是不断获取stream流中的数据,然后执行循环体中的操作。await for一般用在知道Stream什么时候完成,并且必须等待传递完成后才能使用,不然就会一直阻塞。
8、Stream 与 Future的关系?
1、Future表示稍后获得的一个数据,所有异步操作的返回值都用Future来表示。
2、Future只能表示一次异步获取的数据。
3、Stream表示多次异步获取的数据。例如按钮多次点击,这个点击事件就是Stream。
4、Future只返回一 次值,Stream返回多次值。
5、Dart中统一使用Stream流处理数据。
9、Stream 有哪两种订阅模式?分别是怎么调用的?
Stream单订阅和多订阅两种。
Stream 默认处于单订阅模式,所以同一个 stream 上的 listen 和其它大多数方法只能调用一次,调用第二次就会报错。
Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。
10、Flutter中的Widget、State、Context 的核心概念?是为了解决什么问题?
主要是为了解决多个部件之间的交互和部件自身状态的维护。
1、Widget: 在Flutter中,几乎所有东西都是Widget。将一个Widget想象为一个可视化的组件(或与应用可视化方面交互的组件),当你需要构建与布局直接或间接相关的任何内容时,你正在使用Widget。
2、Widget树: Widget以树结构进行组织。包含其他Widget的widget被称为父Widget(或widget容器)。包含在父widget中的widget被称为子Widget。
3、Context: 仅仅是已创建的所有Widget树结构中的某个Widget的位置引用。简而言之,将context作为widget树的一部分,其中context所对应的widget被添加到此树中。一个context只从属于一个widget,它和widget一样是链接在一起的,并且会形成一个context树。
4、State: 定义了StatefulWidget实例的行为,它包含了用于”交互/干预“Widget信息的行为和布局。应用于State的任何更改都会强制重建Widget。
11、Dart异步编程中的 Future关键字
Dart中,执行一个异步任务使用Future来处理。
在 Dart 的每一个 Isolate 当中,执行的优先级为 :Main > MicroTask > EventQueue。
12、什么是Isolate?
Dart只有一个主线程,其实在Dart中并不是叫 Thread ,而是有个专门名词叫 「isolate(隔离)。
用官方文档中定义一句话来概括: An isolated Dart execution context .大概的意思就是「isolate实际就是一个隔离的Dart执行的上下文环境(或者容器)」。「isolate是有自己的内存和单线程控制的事件循环」
其实在Dart也会遇到一些耗时计算的任务,不建议把任务放在主isolate中,否则容易造成UI卡顿,需要开辟一个单独isolate来独立执行耗时任务,然后通过消息机制把最终计算结果发送给主isolate实现UI的更新。」 在Dart中异步是并发方案的基础,Dart支持单个和多个isolate中的异步。。
「Dart没有共享内存的并发」,没有竞争的可能性所以不需要锁,也就不存在死锁的问题。
Flutter异步编程-Isolate
13、Flutter 中的生命周期
13.1 statefulWidget
1、initState→didChangeDependence→build→didupdateDependence→deactivate→dispose
2、initState:只调用一次,widget创建执行的第一个方法,这里可以做初始化工作,不如初始化state变量。
3、didChangeDependence:多次调用。
1、initState调用
2、依赖的InheritedWidget rebuild会被调用。
3、build:多次调用
1、initState调用
2、setState触发的时候调用
4、didupdateDependence:多次调用
组件状态改变的时候调用
5、deactivate:调用一次
当State对象从树中被移除,会调用此回调,会在dispose之前调用。
页面销毁的时候会依次执行:deactivate → dispose
6、dispose:调用一次
当State对象从树中被永久移除时调用;通常在此方法中释放资源。
13.2 statelessWidget
1、builde
2、update
14、flutter和RN
flutter与RN的相同点:
1.都是移动开发跨台解决方案
2.界面的编写都采用响应式视图,维护了一个状态机,只更新改变的最小区域界面
3.都支持热重载hot reload,开发调试非常方便
4.调用系统的service仍然需要封装接口,仍然还是需要懂得native开发
flutter与RN 的区别:
- 性能方面:
Flutter由于是基于Dart语言, 所以避免了RN的那种通过桥接器与Javascript通讯导致效率低下的问题,所以在性能方面比RN更高一筹,会更接近原生的体验. - 学习成本方面:
Flutter是基于Dart语言,相对来说,由于要学习一门新的开发语言所以学习成本比较高, 而RN采用JS语言开发,基于React,对前端工程师更友好. - UI 样式方面:
flutter实现跨平台采用了更为彻底的方案, 因为它基于canvas自己实现了一套UI框架, 所以兼容性更好, 而 RN 在在样式方面还是会遇到比较多的问题,且解决起来会有点麻烦.
4.成熟度的方面:
React Native 是在 2015 年发布的,经过 3 年多的发展,已经比较成熟, 虽然也还不完善, 但是Flutter 是在今年 6 月份才推出发布预览版,社区也刚刚发展, 在github上还有两千多个待解决的问题,所以flutter需要更多时间。
flutter的有一套自己的UI框架,所以兼容性更好,且由于RN需要桥接器和JavaScript通讯,导致效率低下,flutter更接近原生体验。
15、Flutter 线程管理模型 (待加强)
Flutter Engine层会创建一个Isolate,并且Dart代码默认就运行在这个主Isolate上。必要时可以使用spawnUri和spawn两种方式来创建新的Isolate,在Flutter中,新创建的Isolate由Flutter进行统一的管理。
事实上,Flutter Engine自己不创建和管理线程,Flutter Engine线程的创建和管理是Embeder负责的,Embeder指的是将引擎移植到平台的中间层代码。
Flutter 中存在的四大线程:分别为 UI Runner、GPU Runner、IO Runner, Platform Runner (原生主线程) ,在 Flutter 中可以通过 isolate 或者 compute 执行真正的跨线程异步操作。
16、Flutter状态管理
Flutter的状态可以分为全局状态和局部状态两种。
常用的状态管理都是基于InheritedWidget封装的用于Widget树的数据传递与共享的的一套框架。
Provider是继承于InheritProvider,而InheritProvider本质上是一个InheritWidget,所以Provider本质上是依托于InheritProvider的机制来实现的widget树的状态共享。
总结:Provider本质上是依托于InheritProvider的机制来来实现widget树的共享。
17、isolate是怎么进行通信和实例化的?
1、isolate是一个隔离Dart执行的上下文环境。
2、isolate有自己的内容和单线程控制的事件循环。
3、isolate之间的内存逻辑上是隔离的,不共享内存。
4、任何Dart程序的并发都是运行多个isolate的结果。Dart没有共享内存的并发,所以不存在死锁问题。
isolate线程之间的通信主要通过port来进行,这个port消息传递过程是异步的。
18、Future还是isolate场景分析?
1、如果一段代码不会被中断,那么就直接使用正常的同步执行就行。
2、如果代码段可以独立运行而不会影响应用程序的流畅性,建议使用 Future (需要花费几毫秒时间)
3、如果繁重的处理可能要花一些时间才能完成,而且会影响应用程序的流畅性,建议使用 isolate (需要几百毫秒)
使用 isolate 的具体场景:
1、JSON解析: 解码JSON,这是HttpRequest的结果,可能需要一些时间,可以使用封装好的 isolate 的 compute 顶层方法。
2、加解密: 加解密过程比较耗时
3、图片处理: 比如裁剪图片比较耗时
4、从网络中加载大图
19、Flutter 是如何与原生Android、iOS进行通信的?(重点了解)
Flutter 通过 PlatformChannel 与原生进行交互,其中 PlatformChannel 分为三种:
BasicMessageChannel :用于传递字符串和半结构化的信息。
MethodChannel :用于传递方法调用(method invocation)。
EventChannel : 用于数据流(event streams)的通信。
20、Flutter 绘制流程
GPU的VSync信号同步到UI线程,UI线程使用Dart来构建抽象视图结构。然后,这些数据结构在GPU中进行图层合成,视图数据提供给 Skia引擎渲染为 GPU数据,这些数据通过 OpenGL或者 Vulkan提供给 GPU。
21、PlatformView
platform view 就是 AndroidView 和 UIKitView 的总称,允许将 native view 嵌入到了 flutter widget 体系中,完成 Dart 代码对 native view 的控制。
链接:https://www.jianshu.com/p/9306d7ecde35
22、Flutter的理念架构

1、Framework层则是使用Dart编写的一套基础视图库,包含了动画、图形绘制和手势识别等功能,是使用频率最高的一层。
2、Engine层负责图形绘制、文字排版和提供Dart运行时,Engine层具有独立虚拟机,正是由于它的存在,Flutter程序才能运行在不同的平台上。
3、Embedder是操作系统适配层,实现了渲染。
参考:https://www.jianshu.com/p/9064a68a05ae
23、flutter的key和context
1、key:是flutter用来标记widget的唯一标识。
- Local Key(局部key):分为Value Key、Object Key和Unique Key,必须要有唯一性。
Local Key顾名思义,指的是在当前Widget层级下,有唯一的Key属性。可以用int或者string来表示。
1.1 Value Key使用事例如下
使用学生的学号或者身份证号等唯一的标识来标识。

1.2 Object Key
通过对比对象的地址来判断是否相同,下同new了两次Student,所以key是不同的,如果使用同一个对象就会报错。

1.3 Unique Key
实在找不到什么标识的话我们可以使用UniqueKey来进行标记,自动生成唯一的key。

- Global Key (全局key):在全局APP中,具有唯一性。Global Key的性能会比Local Key差很多。
Global Key使用场景,一是让widget在Widget Tree发生大幅改动的时候仍然保留状态,二是像JavaScript里面getElementById那样查找某个元素并得到它的各种信息。
final _globalKey1 = GlobalKey ();
final _globalKey2 = GlobalKey ();

获取全局的数据信息:


2、context:是指widget在Widget树中的具体位置,flutter就是根据context包含的信息,来绘制widget及其对应的位置。
24、Flutter 空安全
空安全(Sound null safety)是Flutter 更新之后 Dart 中新增的一项特性,swift语言也有此特性。有了空安全,Dart 分析器可以进行更好的检查。与空安全相关的新操作符和关键字有 ?、!。
在空安全之前,是可以直接赋值为null,且可以通过编译,这个就说空安全与之前最大的不同,而且是在编译阶段就直接报异常错误。
在使用空安全的情况下,我们想让变量赋值为null时,我们可以这样处理:
int? count = null;
String? name = null;
只需在类型后面添加 ? 即可
类型后面跟操作符 ? 表示当前变量可为null。
总结:
操作符 ? :放在类型后面表示当前变量可为null,例如 String a 和 String ? b ,a 不能为null,而 b 可以。
操作符 !:表示此变量值不为null,如果为null则会抛出异常。使用请慎重考虑。
25、flutter和原生交互传参的方式
Flutter和Xcode混编
重点总结:
1、flutter调用OC传参,需要使用MethodChannel
1、flutter内部点击事件传参
// 用于调用原生方法 "hometestmethod"标识符与OC中的监听标识符保持一致
var homechannelmethod = MethodChannel("hometestmethod");
// 点击
onTap: (){
//给原生发送消息并传入参数,原生根据标识homePageCallNativeMethond来做对应的处理
homechannelmethod.invokeMethod('homePageCallNativeMethond',{"key":"value","key1":"value1"});
},
2、OC接受传参
// 1.创建方法通道对象,用于监听flutter调用原生时的回调,唯一标识“hometestmethod”与flutter要保持一致
FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"hometestmethod" binaryMessenger:flutterViewController.binaryMessenger];
//2. 设置监听回调block,flutter端通过通道调用原生方法时会进入以下回调
[channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
//call的属性method是flutter调用原生方法的方法名,我们进行字符串判断然后写入不同的逻辑
if ([call.method isEqualToString:@"homePageCallNativeMethond"]) {
//flutter传给原生的参数
id para = call.arguments;
NSLog(@"flutter传给原生的参数:%@", para);
//可以做界面跳转
[self.navigationController pushViewController:[TestViewController new] animated:YES];
//获取一个字符串
NSString *nativeFinalStr = @"原生给flutter回传的值";
if (nativeFinalStr!=nil) {
//把获取到的字符串传值给flutter
result(nativeFinalStr);
}else{
//异常(比如改方法是调用原生的getString获取一个字符串,但是返回的是nil(空值),这显然是不对的,就可以向flutter抛出异常 进入catch处理)
result([FlutterError errorWithCode:@"001" message:[NSString stringWithFormat:@"进入异常处理"] details:@"进入flutter的trycatch方法的catch方法"]);
}
}else{
//调用的方法原生没有对应的处理 抛出未实现的异常
result(FlutterMethodNotImplemented);
}
}];
2、OC调用FLuttter传参,需要调用EventChannel,通过代理传参。
//1. 创建事件通道对象,唯一标识 “hometest”,到时flutter是根据该标识来监听原生发送给flutter的参数信息
FlutterEventChannel *evenChannel = [FlutterEventChannel eventChannelWithName:@"hometest" binaryMessenger:flutterViewController.binaryMessenger];
//2. 当原生跳往flutter时会触发下面的- (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)events回调方法,可以在该方法中给flutter传递参数
[evenChannel setStreamHandler:self];
//原生跳转flutter时,会触发该方法,在该方法中可以传递参数给flutter界面,需要注意的是flutter代码中必须写上对应的监听代码,这里才会被执行
- (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)events{
if (events) {
events(@{@"key":@"value"});
}
return nil;
}
26、本地数据存储方式
文件、sharedPreferences、sqlite数据库
和原生通讯方式
Flutter定义了三种不同类型的channel,分别是
eventChannel:用于数据流通信,原生向Flutter传参,通过代理eventSink传参。
methodChannel:flutter向原生传参。定义好方法名
BaseMessageChannel:未使用
27、键盘高度超出解决
Scaffold(
resizeToAvoidBottomPadding: false, //输入框抵住键盘
)
28、ListView报Vertical viewport was given unbounded height错误解决方法
原因:宽高溢出。导致widget不显示
解决:给ListView的shrinkWrap属性设置为ture(收缩)。父视图的大小跟随子组件内容大小。
29、Flutter报setState() or markNeedsBuild() called during build.错误解决办法
错误原因:在运行中,控件和响应事件没有构建完,延时加载就可解决问题
解决方法:使用Future延时
void _addIndex() {
Future.delayed(Duration(milliseconds: 200)).then((e) {
setState(() {});
});
}
}
30、Flutter报setState() called after dispose()错误解决办法
错误解析:防止页面关闭执行setState()方法
解决方法:mounted, mounted 表明 State 当前是否正确绑定在View树中。State的生命周期里面,这个 mounted 属性不会改变,直至 framework 调用 State.dispose,当改变之后, State 对象再也不会调用 build 方法 mounted = false。
31、实现水波纹
InkWell
32、安卓和iOS 控件区别
Flutter提供了两种不同风格安卓的Material 和 iOS的Cupertino风格
33、StatelessWidget的生命周期有哪些,请按生命周期顺序说一下?
只有build,update。
34、Flutter是怎么实现热重载的,说说具体实现原理?
Flutter的 Debug 模式支持 JIT(Just In Time),指的是即时编译或运行时编译,
JIT 编译器将 Dart 代码编译成可以运行在 Dart VM 上的 Dart Kernel, Dart Kernel可以动态更新。
35、为什么说Flutter的性能好,与RN的区别?
1、flutter没有桥接层。
2、自带渲染引擎。skia
3、编译执行。JS是解释执行。
4、Flutter Engine虚拟机
36、Flutter中是怎么实现并发操作的?
两种方式:isolate 和 Future
Future:短时间的几毫秒。
isolate:长时间几百毫秒
37、isolate是怎么进行通信和实例化的?
通信:通过port实现通信,sendPort和reservePort,这个port消息传递过程是异步的。
实例化: Isolate isolate =await Isolate.spawn<SendPort>(dataLoader, mainThreadPort.sendPort);
重点,深刻解析: https://www.bbsmax.com/A/kjdwe1NBJN/
38、Future和isolate有什么区别?
Future: 是一个异步执行并且在未来的某一个时刻完成(或失败)的任务。Future本质上 并非并行执行,而是遵循事件循环处理事件的顺序规则执行。
isolate:为了并行运行代码。每个isolate都有自己的事件循环。
总结:
- 如果一个方法需要几毫秒使用Future
- 如果一个处理流程需要几百毫秒使用isolate
使用isolate场景:
- JSON解码
- 加密
- 图像处理:裁剪
- 从web加载图像
39、Stream与Future是什么关系?
Future:只能接收一次返回的数据
Stream:接收多次返回的数据。flutter就是基于Stream流的处理。
40、setState是同步还是异步操作
setState是异步操作。下面代码的打印先输出2然后在输出1.
onChanged:(value) async{
setState(){
print('1111111') ;
}
print('222222') ;
}
41、setState无效
Flutter开发App时,偶尔会遇到SetState()不起作用或界面更新不完全的Bug,是什么原因导致的呢?
比如在弹窗中点击,标记对号。我们是使用系统的弹窗SimpleDialog来展示,但是点击之后标记框状态没有改变,这是为什么呢?因为SimpleDialog是无状态组件,要解决问题,我们需要包裹StateFulBuilder 如下:
