您的当前位置:首页正文

2024Flutter面试题

2024-10-31 来源:个人技术集锦

1.Dart是值传递还是引用传递?

2.简述Dart语音特性

在Dart中,一切都是对象,所有的对象都是继承自Object
Dart是强类型语言,但可以用var或 dynamic来声明一个变量,Dart会自动推断其数据类型,dynamic类似c#
没有赋初值的变量都会有默认值null
Dart支持顶层方法,如main方法,可以在方法内部创建方法
Dart支持顶层变量,也支持类变量或对象变量
Dart没有public protected private等关键字,如果某个变量以下划线(_)开头,代表这个变量在库中是私有的

3、Dart 是不是单线程模型?是如何运行的?

引用《Flutter中文网》里的话
Dart 在单线程中是以消息循环机制来运行的,其中包含两个任务队列,一个是“微任务队列” microtask queue 另一个是“事件队列” event queue。

4.Dart的事件循环的运行遵循以下规则

入口函数 main()执行完后,消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务,当所有微任务队列执行完后便开始执行事件队列中的任务,事件任务执行完毕后再去执行微任务,如此循环往复

5.那么在Dart中如何让你的代码异步执行呢?

很简单,把要异步执行的代码放在微任务队列或者事件队列里就行了。
可以调用scheduleMicrotask()来让代码以微任务的方式异步执行
可以调用Timer.run来让代码以Event loop的方式异步执行

6.说一下 Future 和 Future队列 ?

Dart中,执行一个异步任务使用Future来处理。在 Dart 的每一个 Isolate 当中,执行的优先级为 :Main > MicroTask > EventQueue。
Dart 在单线程中是以消息循环机制来运行的,其中包含两个任务队列,一个是“微任务队列” microtask queue,另一个叫做“事件队列” event queue。Future 默认情况下其实就是往「事件队列」里插入一个事件,当有空余时间的时候就去执行,当执行完毕后会回调 Future.then(v) 方法。而我们也可以通过使用 Future.microtask 方法来向 「微任务队列」中插入一个任务,这样就会提高他执行的效率。

7.Future和 Stream Isolate?

在 Flutter 中,Future、Stream和Isolate都是用于处理异步任务的重要概念,它们的区别和应用场景如下:

Future:表示一个异步操作的结果。使用Future可以在异步操作完成后获取到操作的结果。适用于处理单个异步任务并获取最终结果的场景,例如网络请求、文件读写等。

Stream:表示一个异步的数据流。使用Stream可以处理异步的数据流,例如网络请求的响应数据、传感器数据等。适用于处理连续的异步数据的场景。

Isolate:是Dart中的线程,代表一个执行环境。不同执行环境(Isolate)之间内存不共享。Dart的异步操作不一定在另一个线程(Isolate)中执行,而且通常是在同一个线程(Isolate)中执行。适用于处理耗时的计算任务、网络请求、处理大量数据等场景。使用Isolate可以提高应用的性能和响应速度。

在实际应用中,可以根据具体的需求选择使用合适的异步方式。例如,如果需要处理单个异步任务并获取最终结果,可以使用Future;如果需要处理异步数据流,可以使用Stream;如果需要在不同的线程中执行异步任务,可以使用Isolate。同时,在使用Isolate时需要注意线程之间的通信和数据传递问题。

8.在flutter里streams是什么?有几种streams?有什么场景用到它?

Stream 用来处理连续的异步操作,Stream 是一个抽象类,用于表示一序列异步数据的源。它是一种产生连续事件的方式,可以生成数据事件或者错误事件,以及流结束时的完成事件
分单订阅流和广播流。
网络状态的监控
stream中执行的异步模式就是StreamMicrotask

9.Stream 有哪两种订阅模式?分别是怎么调用的?

Stream有两种订阅模式:单订阅(single) 和 多订阅(broadcast)。单订阅就是只能有一个订阅者,而广播是可以有多个订阅者。这就有点类似于消息服务(Message Service)的处理模式。单订阅类似于点对点,在订阅者出现之前会持有数据,在订阅者出现之后就才转交给它。而广播类似于发布订阅模式,可以同时有多个订阅者,当有数据时就会传递给所有的订阅者,而不管当前是否已有订阅者存在。
Stream 默认处于单订阅模式,所以同一个 stream 上的 listen 和其它大多数方法只能调用一次,调用第二次就会报错。但 Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。

10.Flutter中的生命周期是什么?有哪些常用的生命周期方法?

在Flutter中,生命周期是指Widget在创建、更新和销毁过程中所经历的各个阶段。每个阶段都有对应的生命周期方法,可以在这些方法中执行一些初始化、清理、监听等操作。
常用的生命周期方法包括:
initState:在Widget第一次插入到Widget树时调用,用于初始化一些数据或监听一些事件。
didChangeDependencies:在Widget依赖的对象发生变化时调用,例如调用了setState方法或父Widget的build方法被调用了。
build:用于构建Widget树,必须返回一个Widget。
didUpdateWidget:在Widget重新构建时调用,可以用于比较新旧Widget是否有差异,并做出相应的处理。
deactivate:在Widget从Widget树中被移除时调用,用于清理一些资源或监听。
dispose:在Widget从Widget树中永久移除时调用,用于释放一些资源或取消监听

11. Flutter中的数据存储是如何实现的?有哪些常用的数据存储方式?

在Flutter中,数据存储是通过Flutter SDK提供的各种存储方式来实现的。常用的数据存储方式包括:
Shared Preferences:用于存储应用程序的轻量级数据,例如用户设置、用户偏好等。
SQLite数据库:用于存储应用程序的结构化数据,例如用户信息、文章列表等。
文件存储:用于存储应用程序的大型文件,例如音频、视频等。

12.flutter的key有几种 如何应用?

Flutter 中的 key 主要分为两类:LocalKey 和 GlobalKey。

LocalKey :

应用于具有相同父 Widget 的 Widget 进行比较。GlobalKey 通常会使用 GlobalKey 来获取某个 widget 对应的 Widget 或 State 或者 Element。

LocalKey主要分为以下几种类型:
ValueKey:当以特定的值作为key时使用,例如一个字符串、数字等。
ObjectKey:将一个对象作为key。
UniqueKey:生成一个唯一的key,但在重新构建后可能会改变
以下是 key 的一些常见应用场景:
1.状态保留:当 Widget 在 Widget 树中移动时,key 可以保留其状态,例如保存用户的滚动位置或在修改集合时保持状态。
2.唯一标识:key 可以作为 Widget、Element 和 SemanticsNode 的唯一标识。
3.性能优化:通过使用 key,Flutter 可以更高效地更新 Widget 树,减少不必要的重新绘制。
4.查找和操作:通过Key,可以在Widget树中查找特定的Widget,并对其进行操作。例如,可以使用GlobalKey:来访问Widget的属性或调用其方法。
5.动画过渡:在进行动画过渡时,使用Key可以帮助Flutter识别新旧Widget之间的关系,以实现平滑的过渡效果。
6.列表更新:在使用ListView、GridView等可滚动列表时,Key用于标识列表中的每个项,以便在更新列表时进行高效的增删改操作。

13.Flutter 如何与 原生Android iOS 通信的?

Flutter可以通过Platform Channels与原生Android和iOS代码通信。Platform Channels是一种消息传递机制,允许Flutter代码与原生平台代码进行双向通信。主要有三种类型的通道:

MethodChannel:用于传递方法调用。
EventChannel:用于数据流的通信,例如持续的传感器数据。
BasicMessageChannel:用于传递字符串和半结构化的消息。

14.Provider、Bloc 和 GetX 是 Flutter 中常用的三种状态管理框架,

它们的区别如下:

Provider:通过 InheritedWidget 传递数据,子组件可以通过 context访问数据
Bloc:使用事件和状态的方式进行数据传递,通过发送事件来更新状态。
GetX:使用控制器来管理状态,通过依赖注入的方式将状态传递给子组件。

响应式:
Provider:本身不具备响应式,需要手动通知子组件更新。
Bloc:基于 Stream 实现响应式,当状态发生变化时,会自动通知订阅者更新。
GetX:通过 .obs 实现响应式,当状态发生变化时,会自动通知依赖该状态的组件更新。

使用场景:
Provider:适用于简单的状态管理场景,如全局配置、主题等。
Bloc:适用于复杂的状态管理场景,如表单验证、异步操作等。
GetX:适用于快速开发、性能要求较高的场景,提供了丰富的 API 和路由管理功能。

15.在 Flutter 中,mixin、extends和implements是三种不同的类关系

,它们的作用和使用场景如下:

mixin(混入):mixin用于在类中添加额外的功能或行为。它可以将一个类的代码片段“混入”到另一个类中,从而实现代码复用。mixin类通常包含一些通用的方法或属性,可以被多个其他类使用。mixin类不能被实例化,只能被其他类通过with关键字混入使用。

extends(继承):extends用于创建子类,子类可以继承父类的属性和方法。父类的私有属性和方法不会被继承,子类可以重写父类的方法来实现自己的逻辑。extends是一种单继承关系,一个类只能有一个直接父类。

implements(实现):implements用于实现接口。接口是一种抽象的定义,它只定义了类需要实现的方法或属性,而不提供具体的实现。类通过implements关键字来声明实现某个接口,并实现接口中定义的方法。

在实际应用中,选择使用哪种关系取决于具体的需求。如果需要在类中添加通用的功能,可以使用mixin;如果需要创建子类并继承父类的功能,可以使用extends;如果需要实现接口的定义,可以使用implements。

16.怎么理解Isolate?


Isolate 是 Dart 中的一种并发模型,它类似于线程,但与线程有本质的区别。Isolate 是一个独立的执行单元,它有自己的堆内存和事件循环,并且与其他 Isolate 之间的内存是隔离的,因此不存在共享内存和锁竞争的问题。
在 Flutter 中,Isolate 主要用于处理耗时操作或需要与主线程异步通信的任务。通过将耗时操作放在 Isolate 中执行,可以避免阻塞主线程,提高应用的响应性和性能。

以下是 Isolate 的一些特点和应用场景:

隔离性:Isolate 之间的内存是隔离的,这意味着一个 Isolate 中的变量和状态不会被其他 Isolate 访问或修改,从而避免了多线程编程中的竞态条件和数据一致性问题。

异步执行:Isolate 可以异步执行任务,这意味着可以在不阻塞主线程的情况下执行耗时操作,提高应用的响应性和性能。

通信:Isolate 之间可以通过发送消息进行通信,这使得不同 Isolate 之间可以协作完成任务。

资源利用:由于 Isolate 是单线程的,因此可以更好地利用多核 CPU 的资源,提高应用的并行处理能力。

在实际应用中,可以根据具体的需求来选择使用 Isolate。例如,如果需要执行耗时的计算任务,可以将其放在 Isolate 中执行,以避免阻塞主线程;如果需要与主线程进行异步通信,可以使用 Isolate 发送消息的方式来实现。
总之,Isolate 是 Flutter 中一种重要的并发模型,它可以帮助开发者更好地处理异步任务和提高应用的性能。

下面列出一些使用
isolate 的具体场景:
1、JSON解析: 解码JSON,这是HttpRequest的结果,可能需要一些时间,可以使用封装好的 isolate 的 compute 顶层方法。
2、加解密: 加解密过程比较耗时
3、图片处理: 比如裁剪图片比较耗时
4、从网络中加载大图

17.描述Flutter的核心渲染模块三棵树

WidgetTree:存放渲染内容、它只是一个配置数据结构,创建是非常轻量的,在页面刷新的过程中随时会重建

Element 是分离 WidgetTree 和真正的渲染对象的中间层, WidgetTree 用来描述对应的Element 属性,同时持有Widget和RenderObject,存放上下文信息,通过它来遍历视图树,支撑UI结构。

RenderObject (渲染树)用于应用界面的布局和绘制,负责真正的渲染,保存了元素的大小,布局等信息,实例化一个 RenderObject 是非常耗能的

当应用启动时 Flutter 会遍历并创建所有的 Widget 形成 Widget Tree,通过调用 Widget 上的 createElement() 方法创建每个 Element 对象,形成 Element Tree。最后调用 Element 的 createRenderObject() 方法创建每个渲染对象,形成一个 Render Tree。

18.Widget、Element、RenderObject三者之间的关系

Widget:是 Flutter 用户界面的不可变描述,用于存储渲染所需的信息,例如布局、样式等。

RenderObject:负责管理布局、绘制等操作,保存了元素的大小、布局等信息。

Element:是实例化的 Widget 对象,通过 Widget 的 createElement()方法生成,是控件树上的实体。

具体来说,Widget 会被 inflate(填充)到 Element 中,并由 Element 管理底层渲染树。Element 是渲染树的节点,它包含了 Widget 的配置信息和对应的 RenderObject。RenderObject 则根据 Widget 的配置进行布局和绘制。

在实际开发中,我们通常通过创建和组合 Widget 来构建用户界面,而 Flutter 框架会自动处理 Widget、Element 和 RenderObject 之间的转换和管理,以实现高效的界面渲染。

19.在 Dart 中,var、dynamic、const和final的区别?

var:声明变量,可以赋值任意对象。var变量在声明时不指定类型,而是在赋值时确定类型。一旦赋值,类型便会确定,不能再改变其类型。

dynamic:声明变量,可以赋值任意对象。与var类似,dynamic声明的变量可以在后期改变赋值类型。

const:必须初始化,其值在编译时确定,只能赋值一次,且不能修改值。赋值必须是常量。const对象无法访问运行时需要计算的任何内容。

final:必须初始化,其值在运行时确定,只能赋值一次,且不能修改值。赋值可以是常量也可以是变量。final和const可以与变量的数据类型一起使用,也可以与var关键字一起使用

20.什么是widget? 在flutter里有几种类型的widget?分别有什么区别?

能分别说一下生命周期吗?

在 Flutter 中,Widget 是构建用户界面的基本单位。它可以表示 UI 元素,也可以表示功能性组件,如用于手势检测的 GestureDetector Widget、用于应用主题数据传递的 Theme 等。
Flutter 中的 Widget 分为 StatelessWidget 和 StatefulWidget 两种类型,它们的区别在于是否管理状态。

StatelessWidget 不需要管理状态,它的生命周期只有一个方法 build(),用于构建 UI。
StatefulWidget 需要管理状态,它的生命周期包括构造方法、initState、didChangeDependencies 和 build 等方法。在 StatefulWidget 中,可以通过 setState 方法来更新状态。
总的来说,StatelessWidget 适用于不需要状态管理的场景,而 StatefulWidget 适用于需要状态管理的场景。

21.flutter 局部刷新有几种方式?

在 Flutter 中,局部刷新可以通过以下几种方式实现:

使用 StatefulBuilder:StatefulBuilder 是 Flutter 官方提供的局部刷新组件。它可以根据状态的变化来刷新子组件,而无需重新构建整个页面。

使用 StreamBuilder + StreamController:StreamBuilder 可以监听流的变化,并根据流的最新数据来刷新子组件。

使用 FutureBuilder & StreamBuilder:FutureBuilder 可以监听 Future 的完成情况,并根据 Future 的结果来刷新子组件。

使用 provider(异步通信):provider 是一个状态管理库,可以方便地管理应用的状态,并在状态变化时触发子组件的刷新。

使用 Flutter ValueNotifier 异步通信、ValueListenableBuilder:ValueNotifier 可以监听值的变化,并在值发生变化时触发子组件的刷新。

22.介绍下Flutter的理念架构?

Flutter 是一个跨平台的 UI 工具包,它的设计目的是允许跨 iOS 和 Android 等操作系统的代码重用,同时也允许应用程序直接与底层平台服务对接。其目标是让开发者能够交付在不同平台上感觉自然的高性能应用,在尽可能多的代码共享的同时,拥抱存在差异的地方。

Flutter 的架构可以分为三层:

Framework 是使用 Dart 编写的一套基础视图库,包含了动画、图形绘制和手势识别等功能,是开发者使用频率最高的一层。

Engine 是 Flutter 的核心引擎,使用 C++编写,提供了图形绘制、文字排版和 Dart 运行时等功能,使得 Flutter 应用程序可以跨平台运行。

Embedder 是操作系统适配层,负责将 Flutter 嵌入到不同的平台上,主要工作包括渲染 Surface 设置、线程设置、事件循环以及插件的平台适配等。

23.await for 如何使用?


await for是不断获取stream流中的数据,然后执行循环体中的操作。它一般用在直到stream什么时候完成,并且必须等待传递完成之后才能使用,不然就会一直阻塞。

24.介绍下 Widget、State、Context 概念?

Widget(组件):Widget 是 Flutter 构建用户界面的基本单位。它定义了界面的结构和外观。Widget 可以是简单的文本、按钮,也可以是复杂的布局组件等。

State(状态):State 用于管理 Widget 的动态数据和状态变化。当状态改变时,关联的 Widget 会进行相应的重新构建,以反映状态的更新。State 通常与 StatefulWidget 相关联。

Context(上下文):Context 提供了关于 Widget 在整个 Widget 树中的位置和相关信息的访问。它用于在 Widget 之间传递数据、获取主题等,通过 Context 可以方便地在不同层次的 Widget 之间进行通信和共享信息。

25.flutter的设计模式有哪几种?

单例模式:确保一个类只有一个实例,并提供全局访问点。
状态模式:通过不同状态类来处理对象在不同状态下的行为。
观察者模式:用于实现组件之间的通知和监听机制。
工厂模式:创建对象时通过工厂类来决定具体创建哪种类型的对象。
代理模式:可以对某些操作进行代理和控制。
组合模式:将多个对象组合成一个树状结构进行统一管理和操作。

26.如何获取控件的大小和位置?

创建一个GlobalKey对象,并将其与要获取大小和位置的控件关联。
在需要获取控件大小和位置的地方,使用GlobalKey对象的currentContext.findRenderObject()方法获取对应的RenderBox对象。
通过RenderBox对象的size属性获取控件的大小。
通过RenderBox对象的offset属性获取控件的位置。

27.Flutter 渲染流程是什么?(GPU)

Flutter只关心向 GPU提供视图数据,GPU的 VSync信号同步到 UI线程,UI线程使用 Dart来构建抽象的视图结构,这份数据结构在 GPU线程进行图层合成,视图数据提供给 Skia引擎渲染为 GPU数据,这些数据通过 OpenGL或者 Vulkan提供给 GPU。

28.怎么减少Widget的重新构建?

1,首先要做的就是将大的Widget树重构成较小的单个的Widget,每一个Widget都有它自己的build方法。
2,尽可能的使用const构造函数,这将告知Flutter不需要重建这个widget。
3,使stateful widget的子树尽可能的小,如果stateful widget有一个widget子树,那么为这个stateful widget创建一个自定义widget,并为其提供一个child参数。

29.什么是BuildContext,它有什么用?

1,Widget 树定位:它表示当前 Widget 在整个 Widget 树中的位置和上下文信息,通过它可以确定,2个 Widget 在树中的层次和相关关系。
2,数据传递和获取:可以利用 BuildContext 来获取父级传递下来的各种数据,比如主题、路由信息等。
3,资源查找:能够基于此找到附近范围内的资源,例如特定的样式、本地化字符串等。
4,导航和路由操作:在进行页面导航等操作时需要用到 BuildContext 来执行相关操作。

30. Flutter中的动画是如何实现的?有哪些常用的动画类?
在Flutter中,动画是通过Animation和AnimationController两个类来实现的。Animation表示动画的当前状态,例如动画的当前值、是否完成、是否反向等。AnimationController用于控制动画的开始、暂停、恢复、反向等。

Flutter中的动画可以分为两种类型:显式动画和隐式动画。显式动画是通过AnimationController控制的,例如Tween动画、Curve动画等。隐式动画则是通过Flutter框架自动执行的,例如AnimatedContainer、AnimatedOpacity等。

常用的动画类包括:
Tween:用于在两个值之间进行插值运算,例如在0和1之间插值计算出当前值。
Curve:用于定义动画的速度曲线,例如线性曲线、抛物线曲线、弹性曲线等。
AnimationController:用于控制动画的开始、暂停、恢复、反向等。
AnimatedBuilder:用于在动画变化时自动重建Widget树,可以用于创建复杂的动画效果。
AnimatedContainer:用于创建一个可以自动执行动画的Container。
AnimatedOpacity:用于创建一个可以自动执行动画的Opacity。

31.新版本flutter和老版本的区别有哪些?

1,支持更多平台:Flutter 3.0 增加了对 Linux 和 macOS 桌面应用程序开发的稳定支持,实现了横跨 iOS、Android、Web、Windows、macOS、Linux 六大平台的开发。
2,性能提升:改进了内部架构,以支持更快的渲染速度和更高的性能。
3,新的语言功能:Flutter 3.0 基于 Dart 2.13 语言,包含了一些新的语法特性。
4,Material You 支持:提供了动态配色方案和更新的视觉组件。
5,生产力更新:改进了许多基础功能,提高了开发效率。

Dart 2.13 为 Flutter 3.0 带来了一些新的语法特性,包括:

Null safety:空安全特性可以帮助开发者避免一些日常开发中很难被发现的错误,并且改良性能。Flutter 2.2.0(2021 年 5 月 19 日发布)之后的版本都要求使用 null safety。
late 关键字:用于延迟初始化,允许在构造函数之外通过函数赋值。
空类型声明符?:表示一个可空类型,可以将 null 赋值给该类型的变量。
非空断言!:用于非空类型的断言,如果变量为空则抛出异常。
required 关键字:允许根据标记的任何命名参数(类或者函数),使得他们不为空。可选参数中必须有个 required。

32.flutter开发中遇到了哪些比较棘手的问题,你是怎么解决的?

在 Flutter 开发中可能遇到一些棘手问题,以下是一些常见的及可能的解决方法示例:

问题:内存泄漏。
解决方法:仔细检查 State 对象的管理,确保正确释放资源,使用工具如内存分析工具来排查问题点。

问题:复杂动画效果实现困难。
解决方法:深入研究 Flutter 的动画框架,参考官方文档和示例,可能需要对动画的逻辑和参数进行精细调整。

问题:与特定平台的兼容性问题。
解决方法:查看平台相关的文档和说明,针对性地调整代码或使用特定的插件来解决。

问题:性能优化瓶颈,如卡顿。
解决方法:分析性能瓶颈点,可能涉及到优化布局结构、减少不必要的计算、合理使用缓存等。

Top