flutter面试集锦
1. flutter是什么?与Dart什么关系?flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面;
Dart是一门面向对象的编程语言,它是开源的类C的语法.
2. flutter页面间如何传值?
动态路由、InheritedWidget、Notification、Eventbus.
4. Dart中var、dynamic与Object的区别?
使用var声明变量,一旦完成赋值操作其数据类型也就被确定,期间不可更改数据类型,dart会在编译阶段根据该变量的赋值自动推断该变量的数据类型;
dynamic是动态数据类型,可以改变其数据类型,运行时由系统根据该变量的赋值自动推断该变量的数据类型,在编译时会使Dart语言的静态语法检查失效(如调用一个不属于字符串对象的方法,不会报错),但会在运行时报错.
而Object是所有对象的基类,其数据类型是Object类型,不能调用Object类中不存在的方法.
5. const和final的区别?
const的值在编译期确定,而final的值在运时确定. 两者一旦初始化,则都不允许再次发生更改.
6. Dart中??与??=的区别?
两者都是dart中的操作符,??表示如果为空则返回,??=表示如果为空则赋值.
7. flutter数据持久化方式有哪些?
Shared_Preferences、SQLite、文件等.
8. flutter中的GlobalKey是什么,有什么作用?
GlobalKey可以获取到对应的Widget的State对象,当我们页面内容很多时,而需要改变的内容只有很少的一部分且在树的底层的时候,我们可以通过GlobalKey获取对应的state来增量更新.
9. Widget、Element、RenderObject三者之间的关系?
widget&&element&&render
根据Widget树生成一个Element树,Element树中的节点都继承自Element类.
根据Element树生成Render树(渲染树),渲染树中的节点都继承自RenderObject类.
根据渲染树生成Layer树,然后上屏显示,Layer树中的节点都继承自Layer类.
Widget树和Element树节点是一一对应关系,每一个Widget都会有其对应的Element,但是RenderObject树则不然,只有需要渲染的Widget才会有对应的节点.
Element树相当于一个中间层,它对Widget和RenderObject都有引用.当Widget不断变化的时候,将新Widget拿到Element来进行对比,看一下和之前保留的.
Widget类型和Key是否相同,如果不一样,则重新创建Element和RenderObject,否则只需要更新里面的一些属性即可,以最小的开销更新RenderObject,引擎在解析RenderObject的时候,也就达到了高性能渲染的目的.
10. 通过BoxDecoration和ClipRRect设置圆角有什么区别?
使用BoxDecoration设置圆角不会影响其child控件,也就是如果child是图片或者也有背景色的话,那么圆角效果就会失效.
而ClipRRect是会影响到child控件的,加了圆角后,也会约束到child产生圆角效果.
11. 阐述state的生命周期?
生命周期
initState-->didChangeDependencies-->build-->(didUpdateWidge->)deactivate->dispose
initState:
state创建初始化时调用,表示state将和一个BuildContext产生关联,需要注意的是此时BuildContext还没有完全加载完成,如果需要获取BuildContext及监听第一次build完成可以在下面回调中获取;
widgetsBinding = WidgetsBinding.instance;widgetsBinding.addPostFrameCallback((callback){print("addPostFrameCallback be invoke");});
didChangeDependencies:
在initState()之后调,当State对象的依赖关系发变化时,该法被调,初始化时也会被调;
deactivate:
当state暂时在视图树中移除时被调用,页面切换时也会被调用;
dispose:
state销毁时调用,在调用此方法之前会先调用deactivate();
didUpdateWidget:
当widget状态发生变化时调用。
12. Flutter与原生通信的Channel有哪几种?
Flutter通过PlatformChannel与原生进行交互,其中PlatformChannel分为三种:
MethodChannel:
用于传递方法调用(method invocation),Flutter与Native端相互调用,调用后可以返回结果,可以Native端主动调用,也可以Flutter主动调用,属于双向通信.此方式为最常用的方式,Native端调用需要在主线程中执行.
BasicMessageChannel:
用于使用指定的编解码器对消息进行编码和解码,属于双向通信,可以Native端主动调用,也可以Flutter主动调用.
EventChannel: 用于数据流(event streams)的通信,Native端主动发送数据给Flutter,通常用于状态的监听,比如网络变化、传感器数据等.
13、Flutter中主要有哪些线程?
UI Runner 、 GPU Runner、IO Runner、Platform Runner;
14. flutter、uniapp、unity的优缺点?
flutter:
优点:
1.热重载(Hot Reload),利用Android Studio直接一个ctrl + s就可以保存并重载,模拟器立马就可以看见效果,相比原生冗长的编译过程强很多;
2.一切皆为Widget的理念,对于Flutter来说,手机应用里的所有东西都是Widget,通过可组合的空间集合、丰富的动画库以及分层可扩展的架构实现了富有感染力的灵活界面设计;
3.借助可移植的GPU加速的渲染引擎以及高性能本地代码运行时达到跨平台设备的高质量用户体验.最终结果就是利用Flutter构建应用在运行效率上会和原生应用差不多.
缺点:
1.不支持热更新;
2.三方库有限,需要自己造轮子;
3.Dart语言编写,增加了学习难度,并且学习了Dart之后无其他用处,相比JS和Java来说.
uni-app:
uni-app 是内置了JSCore用于运行JS代码,然后通过WebView进行渲染,同时内部内置了一个基于Weex改进的桥接框架,从而可以实现把JS 代码转为平台原生的控件去渲染,利用了原生渲染能力
uni-app利用的是WebView或者原生控件的桥接,本身并不实现渲染引擎,它的mpvue、Weex、JSCore都是其他的第三方开源框架,而uni-app 实现了集成化平台,所以它的优势就是在于对前端开发而言,只要学会vue,就可以通过它简单开发app,同时它还能帮你转译成小程序,另外因为是JS文本代码,支持code-push这种热更新也不算违规。
unity:
unity: 3D游戏引擎,本身主要是用于游戏场景,有时也被运用于AR或者地图导航的场景,使用它开发app的可能性很低.
总结:
uni-app: 更适合前端开发,利用vue的能力构建简单场景的app或者小程序.
flutter: 更贴近原生开发的需求,拥有更好的性能和跨端能力.
unity: 主要是服务于游戏和3D渲染需求.
15. 简述Widget的StatelessWidget和StatefulWidget两种状态组件类?
StatelessWidget: 无状态组件,状态不可改变,除了依赖于自身的配置信息(在父节点构建时提供)外不再依赖于任何其他信息,比如典型的Text、Row、Column、Container 等,都是StatelessWidget.生命周期: 初始化、通过 build()渲染.
StatefulWidget: 有状态组件,持有的状态可以在Widget生命周期中发生改变,比如复选框、Button 等.
16. flutter在Debug和Release下分别使用什么编译模式,有什么区别?
Debug模式下使用JIT编译模式,即Just in time(即时编译),Release下使用AOT模式,即Ahead of time(提前编译).JIT模式因为需要边运行边编译,所以会占用运行时内存,导致卡顿现象,但是有动态编译效果对于开发者来说非常方便调试.AOT模式提前编译不会占用运行时内存,相对来说运行流畅,但是会导致编译时间增加.
17. Flutter出现异常时如何友好的提示用户?
使用ErrorWidget.builder进行全局设置自定义界面即可.
ErrorWidget.builder = (FlutterErrorDetails errorDetails) { return handleErrorWidget(context, errorDetails);};18. Dart是不是单线程模型?是如何运行的?
Dart在单线程中是以消息循环机制来运行的,包含两个任务队列,一个是“微任务队列” MicroTask Queue,另一个叫做“事件队列” Event Queue.
当Flutter应用启动后,消息循环机制便启动了.首先会按照FIFO的顺序逐个执行微任务队列中的任务,当所有微任务队列执行完后便开始执行事件队列中的任务,事件任务执行完毕后再去执行微任务,如此循环往复,生生不息.
在Dart每一个isolate当中,执行优先级为: Main > MicroTask > EventQueue
Dart运行流程
19. Dart 是如何实现多任务并行的?
Dart 是单线程的,不存在多线程,Dart的多线程和前端的多线程有很多的相似之处.flutter的多线程主要依赖Dart的并发编程、异步和事件驱动机制.
20. Future、async 和 await?
Future是异步编程的解决方案,Future是基于观察者模式的,它有 3 种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)可以用then方法指定成功状态的回调函数then 方法还可以接受一个可选命名参数,参数的名称是onError,即失败状态的回调函数Future 实例的函数中抛出了异常,被onError回调函数捕获到,并且可以看出then方法返回的还是一个 Future对象,所以其实我们还可以利用Future对象的cathError进行链式调用从而捕获异常;
在Dart1.9中加入了async和await关键字,有了这两个关键字,我们可以更简洁的编写异步代码,而不需要调用Future相关的API.
将async关键字作为方法声明的后缀时,具有如下意义:
被修饰的方法会将一个 Future 对象作为返回值;
该方法会同步执行其中的方法的代码直到第一个 await 关键字,然后它暂停该方法其他部分的执行;
一旦由 await 关键字引用的 Future 任务执行完成,await的下一行代码将立即执行;
async 不是并行执行,它是遵循Dart 事件循环规则来执行的,它仅仅是一个语法糖,简化Future API的使用.
21. Dart异步编程中的 Stream数据流?
在Dart中,Stream和Future一样,都是用来处理异步编程的工具.它们的区别在于Stream可以接收多个异步结果,而Future只有一个.
Stream的创建可以使用Stream.fromFuture,也可以使用StreamController来创建和控制.还有一个注意点是: 普通的Stream只可以有一个订阅者,如果想要多订阅的话,要使用 asBroadcastStream().
22. Stream 有哪两种订阅模式?分别是怎么调用的?
Stream有两种订阅模式: 单订阅(single)和多订阅(broadcast).
单订阅就是只能有一个订阅者,而广播是可以有多个订阅者。这就有点类似于消息服务(Message Service)的处理模式。单订阅类似于点对点,在订阅者出现之前会持有数据,在订阅者出现之后就才转交给它。
而广播类似于发布订阅模式,可以同时有多个订阅者,当有数据时就会传递给所有的订阅者,而不管当前是否已有订阅者存在.
Stream 默认处于单订阅模式,所以同一个 stream 上的listen和其它大多数方法只能调用一次,调用第二次就会报错.但 Stream 可以通过 transform()方法(返回另一个 Stream)进行连续调用.通过Stream.asBroadcastStream()可以将一个单订阅模式的Stream转换成一个多订阅模式的Stream,isBroadcast属性可以判断当前Stream所处的模式.
23. 什么是状态管理,你了解哪些状态管理框架?
状态其实是一个概念上的东西,区分全局状态和局部状态.
局部状态比如说一个控件中输入的信息,全局状态比如是登陆后从后台请求回来的 userId。
当全局状态越来越多,多个页面共享一个状态时,我们就需要管理它。常用的状态管理有BLoC、Provider、getX.
24. mixin机制?
mixin是Dart2.1加入的特性,以前版本通常使用abstract class代替.简单来说,mixin是为了解决继承方面的问题而引入的机制,Dart为了支持多重继承,引入了mixin关键字,它最大的特殊处在于:mixin定义的类不能有构造方法,这样可以避免继承多个类而产生的父类构造方法冲突。mixins的对象是类,mixins绝不是继承,也不是接口,而是一种全新的特性,可以mixins多个类,mixins的使用需要满足一定条件。
mixins类只能继承自object;mixins类不能有构造函数;一个类可以mixins多个mixins类;可以mixins多个类,不破坏Flutter的单继承.
25. Flutter中进行性能监控和如何进行优化?
性能图层(Performance Overlay)ProfileDevTools
26. TODO:Flutter中的手势事件传递、事件分发、事件冲突竞争,滑动流畅等等
AbsorbPointer是吸收拦截,absorbing为true时,拦截下面的监听.IgnorePointer是忽略拦截,为true时,周围控件都拦截,但是stack布局后面的可以得到监听事件.
每个控件都可以加 Listener监听,里面的方法可以实现手势,用controller滑动,从而达到拦截等操作.
GestureDetector经常用来添加点击、双击、滑动、长按等操作,其实也可以定义拦截,滑动等事件.但是其他控件响应了,会直接cancel掉.
27. Widget、State、Context 概念
Widget:在Flutter中,几乎所有东西都是Widget.将一个Widget想象为一个可视化的组件(或与应用可视化方面交互的组件),当你需要构建与布局直接或间接相关的任何内容时,你正在使用Widget.
Widget树: Widget以树结构进行组织.包含其他Widget的widget被称为父Widget(或widget容器).包含在父widget中的widget被称为子Widget.
Context:仅仅是已创建的所有Widget树结构中的某个Widget的位置引用.简而言之,将context作为widget树的一部分,其中context所对应的widget被添加到此树中.一个context只从属于一个widget,它和widget一样是链接在一起的,并且会形成一个context树.
State:定义了StatefulWidget实例的行为,它包含了用于”交互/干预“Widget信息的行为和布局.应用于State的任何更改都会强制重建Widget.
28. 如何获取Widget的尺寸?
想要获取widget的尺寸,必须要等widget的layout结束之后才能取到,目前有三种方式:
通过BuildContext获取,过早获取可能为空,需要延迟获取.
var size = context?.findRenderObject()?.paintBounds?.size;
通过GlobalKey获取.
GlobalKey _globalKey = GlobalKey();var size = _globalKey.currentContext?.findRenderObject()?.paintBounds?.size;
通过SizeChangedLayoutNotifier获取.
使用SizeChangedLayoutNotifier方式,Widget会在layout结束之后会发出一个LayoutChangedNotification通知,我们只需要接收这个通知,即可获取尺寸信息.
29.flutter中如何自定义Widget?
在Flutter中自定义组件有三种方式:通过组合其它组件、自绘和实现RenderObject.
自定义组件方法简介
30.什么是Key?什么时候需要Key?Key的种类有哪些?
key的作用是:控制weidget树上的widget是否被替换(刷新)
当你想要跨widget树保留状态时 , 应该使用key.当修改相同类型的widget集合时, 要将key放在要保留的widget的树的顶部.并且要根据widget中存储的数据类型选择对应的key.
Key分类
Key分类
LocalKey 在具有相同父元素的Elements中,键必须唯一;GlobalKey 在整个APP中唯一的键.
GlobalKey有两种用途,它允许widget在App的任何位置更改父级而不会丢失状态,或者可以使用它们在Widget Tree完全不同的部分中访问有关另一个widget的信息.
比如要在两个不同的屏幕显示相同的widget,并保持所有相同的状态, 此时可以使用GlobalKey.
而LocalKey又有以下几个子类:
ValueKey使用特定类型的值标识自己的键;ObjectKey从对象中获取其标识的键用作其值;UniqueKey仅等于自身的Key;PageStorageKey ValueKey的子类,它定义了PageStorage值的保存位置.
PageStorageKey是用来存储用户滚动位置的专用key,因此APP可以保留它以供后面使用.
我们要注意3点:
在具有相同父元素的Elements中,键必须唯一.相比之下,GlobalKeys在整个应用程序中必须唯一.Key的子类应该是LocalKey或GlobalKey的子类.GlobalKey更为昂贵,因此如果没有必要,请使用ValueKey, ObjectKey, 或者 UniqueKey.
31.简述Provider的核心原理?
Provider的原理和流程图
跨组件状态共享(Provider)
32.BloC原理?
(https://www.jianshu.com/p/19ef6faf2073)
33.mixin extends implement 之间的关系?
继承(关键字 extends)、混入 mixins (关键字 with)、接口实现(关键字 implements).
这三者可以同时存在,前后顺序是extends -> mixins -> implements.
Flutter中的继承是单继承,子类重写超类的方法要用@Override,子类调用超类的方法要用super.
在Flutter中,Mixins是一种在多个类层次结构中复用类代码的方法.mixins的对象是类,mixins绝不是继承,也不是接口,而是一种全新的特性,可以mixins多个类,mixins的使用需要满足一定条件.weww
页:
[1]