flutter-2024面试题
2024最新Flutter面试题分享(含答案解析)
2024-04-113,671阅读10分钟
Dart部分
String扩展一个方法
- 使用关键字
extension ... on为String定义一个扩展类 - 在为扩展类添加一个 新的方法
- String类型对象调用这个扩展的方法
dart
体验AI代码助手
代码解读
复制代码
extension StringExt on String{ // 2. 扩展方法 int add(int x, int y){ return x + y; } } void mian(){ String str = 'hello'; // 3. 使用扩展类方法 int result = str.add(3, 7); debugPrint(result.toString()); } // 单元测试 import 'package:flutter_start/extTest.dart'; import 'package:flutter_test/flutter_test.dart'; void main(){ test('StringExt', (){ String ext = 'ext'; expect(ext.add(3, 7), 10); }); }

dart是单继承还是多继承?
单继承
dart如何达到多继承的效果?
Dart中使用Mixins,可以达到多继承的效果
mixin混入有什么特点
- 作为mixins的类只能
继承自Object,不能继承其他类 - 作为mixins的类
不能有构造函数 - 一个类可以mixins
多个mixins类 - mixins绝不是继承,也不是接口,而是一种全新的特性
dart
体验AI代码助手
代码解读
复制代码
// 类D 继承A和B 关键字 with class D extends A with B{ } // mixin 的使用 class A { String info="this is A"; void printA(){ print("A"); } void run(){ print("A Run"); } } class B { void printB(){ print("B"); } void run(){ print("B Run"); } } class C extends Person with B,A{ C(String name, num age) : super(name, age); }
混入相同方法的多个混入,最终会执行哪一个?
后面的类中的方法将前面的类中相同的方法覆盖
dart运行机制是什么样的?
消息循环机制
- 两个队列,微任务队列和事件队列。
- microtask queue 的优先级高于event queue。
- 在每一次事件循环中,Dart总是先去第一个microtask queue中查询是否有可执行的任务,如果没有,才会处理后续的event queue的流程。
如何向事件队列插入任务?
Future就是将任务插入到事件队列
向微任务队列插入任务
Future.microtask()scheduleMicrotask()Stream中的执行异步的模式就是scheduleMicrotask。因为microtask的优先级又高于event。所以,如果 microtask 太多就可能会对触摸、绘制等外部事件造成阻塞卡顿。
Future和Stream有什么区别?
- Future中的任务会加入下一轮事件循环,而Stream中的任务则是加入微任务队列
- Future 用于表示单个运算的结果,而 Stream 则表示多个结果的序列。
Stream 有同步流和异步流之分。它们的区别在于同步流会在执行 add,addError 或 close 方法时立即向流的监听器 StreamSubscription 发送事件,而异步流总是在事件队列中的代码执行完成后在发送事件。`
Stream订阅模式有哪几种?
Stream分为Single Subscription和Broadcast两种类型, 前者只允许订阅(listen)一次,后者允许多次订阅。
单订阅在订阅者出现之前会持有数据,在订阅者出现之后就才转交给它。
广播订阅可以同时有多个订阅者,当有数据时就会传递给所有的订阅者,而不管当前是否已有订阅者存在。
Stream单订阅,多次订阅会出现什么结果?
会报错,单订阅只能有一次订阅.
即使取消了第一个监听器,也不允许在单订阅流上设置其他的监听器。
Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。
通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。
dart是单线程还是多线程?
Dart是单线程模型。
Dart是如何实现多任务并行的
主要依赖dart的并发编程(Isolate)、异步和事件驱动机制
Isolate
- 在dart中,一个Isolate对象其实就是一个Isolate执行环境的引用,一般来说我们都是通过当前的Isolate去控制其他的Isolate完成彼此之间的交互,而当我们想要创建一个新的Isolate可以使用
Isolate.spawn方法获取一个新的Isolate对象,两个Isolate之间使用SendPort相互发送消息,而Isolate中也存在了一个与之对应ReceivePort接收消息用来处理,但是我们需要注意的是SendPort和ReceivePort在每一个Isolate都有一对,只有同一个Isolate中的ReceivePort才能接受当前类的SendPort发送的消息并且处理。 - Isolate可以把它理解为Dart中的线程。但它又不同于线程,更恰当的说应该是微线程。它与线程最大的区别就是不能共享内存,因此也不存在锁竞争问题,两个Isolate完全是两条独立的执行线,且每个Isolate都有自己的事件循环,它们之间只能通过发送消息通信,所以它的资源开销低于线程。
dart
体验AI代码助手
代码解读
复制代码
@override void initState() async { super.initState(); // 主Isolate的ReceivePort ReceivePort receivePort = ReceivePort(); SendPort? otherSendPort; // 主Isolate接收到子Isolate中由主Isolate的SendPort发送过来的消息 receivePort.listen((message) { if(message is SendPort){ otherSendPort = message; }else{ // 处理消息 // ...... // 子Isolate的SendPort在主Isolate中向子Isolate发送消息 otherSendPort?.send('我是来自主Isolate的消息'); } }); // 创建子Isolate Isolate isolate = await Isolate.spawn((message) { // message 是主Isolate的SendPort // 在子Isolate中创建一个新的ReceivePort ReceivePort recPort = ReceivePort(); // 主Isolate的SendPort SendPort? mainSendPort; // 运用主Isolate的SendPort将子Isolate的SendPort发送给主Isolate message.send(recPort.sendPort); // 子Isolate监听接收到主Isolate那边发送的消息 谁发送?子Isolate的SendPort recPort.listen((msg) { if(msg is SendPort){ mainSendPort = msg; }else{ // 主Isolate的SendPort向主Isolate发送消息 mainSendPort?.send('我是来自子Isolate的消息'); } }); }, receivePort.sendPort);// 参数二 将主Isolate的SendPort传递给子Isolate }
await for如何使用?
await for是用来不断获取stream流中的数据,然后执行循环体中的操作。它一般用在直到stream什么时候完成,并且必须等待传递完成后才能使用,不然会阻塞。
dart
体验AI代码助手
代码解读
复制代码
Stream<String> stream = new Stream<String>.fromIterable(['1', '2', '3','4']); main() async{ await for(String s in stream){ print(s); } }
如何实现websocket稳定连接?
- 定期发送心跳包,一般1秒一次
- 捕获关闭连接事件并重连websocket
- 实现断线重连
Flutter部分
A、B两个组件在setState前修改背景颜色,是否会修改成功?
会
Flutter 渲染流程是什么?(GPU)
Flutter只关心向 GPU提供视图数据,GPU的 VSync信号同步到 UI线程,UI线程使用 Dart来构建抽象的视图结构,这份数据结构在 GPU线程进行图层合成,视图数据提供给 Skia引擎渲染为 GPU数据,这些数据通过 OpenGL或者 Vulkan提供给 GPU。

Widget、Element、RenderObject三者关系是什么?
简述
widget是用于描述Element配置信息的,flutter中一切都是widget,尺寸、颜色、组件等都是widget
element是widget树上特定位置的实例
renderobject是渲染上的一个对象
依赖关系:Element树依赖Widget树,渲染树依赖Element树,最终的UI树是由独立的Element节点构成。
一个widget会创建一个element
一个element持有一个widget和render object,element
会对比widget的变化,将那写需要更新和重建的widget,同步到render object树,以最小的开销来渲染
一、它们是什么?
- Widget:对一个Element配置的描述,刷新的过程中随时会重建。(不参与真正的渲染,widget的属性是不可以改变的,要想改变只能重新创建一个widget对象)
- Element:表示一个Widget树中特定位置的实例,用于对比widget,找出需要更新和重建的widget,更新Element树和RenderObject树。
- RenderObject:渲染树上的一个对象,用于界面的布局和绘制,负责真正的渲染,实例化一个 RenderObject 是非常耗能。
二、关系
- 一个Widget会创建一个Element对象,是通过createElement()创建的。
- 一个Element持有一个RenderObject和一个Widget。Widget 、Element都不负责最后的渲染,真正负责的渲染只有 RenderObject。
- Widget 具有不可变性,但 Element 却是可变的。Element 树将 Widget 树的变化做了抽象,可以只将真正需要修改的部分同步到真实的 RenderObject 树中,最大程度降低对真实渲染视图的修改,提高渲染效率,而不是销毁整个渲染视图树重建。
flutter生命周期,setstate会执行哪些生命周期?
setState()执行后,会执行build()
-
app的状态:AppLifecycleState
inactive:活跃可见paused:关闭或者切换到后台时,不可见的状态hidden:后台运行状态resumed:切回到前台可见状态detached:关闭状态 -
Flutter SDK 3.13 之前的方式:
with WidgetsBindingObserver
在initState()中注册 WidgetsBinding.instance.addObserver(this);
在dispose()移除 WidgetsBinding.instance.removeObserver(this);
在didChangeAppLifecycleState()回调中,检测app的状态 -
Flutter SDK 3.13 之后的方式:
AppLifecycleListener
dart
体验AI代码助手
代码解读
复制代码
late final AppLifecycleListener _listener; @override void initState() { super.initState(); // Initialize the AppLifecycleListener class and pass callbacks _listener = AppLifecycleListener( onStateChange: _onStateChanged, ); } @override void dispose() { // Do not forget to dispose the listener _listener.dispose(); super.dispose(); } // Listen to the app lifecycle state changes void _onStateChanged(AppLifecycleState state) { switch (state) { case AppLifecycleState.detached: _onDetached(); case AppLifecycleState.resumed: _onResumed(); case AppLifecycleState.inactive: _onInactive(); case AppLifecycleState.hidden: _onHidden(); case AppLifecycleState.paused: _onPaused(); } }
StatefulWidget 的生命周期方法
createState:可以调用多次
State 对象的生命周期方法
dart
体验AI代码助手
代码解读
复制代码
class _MyWidgetState extends State<MyWidget> { @override void initState() { super.initState(); // 在 State 对象被插入树中时调用,这个方法只会被调用一次。 } @override void didChangeDependencies() { super.didChangeDependencies(); // 在 initState 之后调用,表示 State 对象的依赖关系发生变化。 } @override Widget build(BuildContext context) { // 在此构建 Widget 树 return Container(); } @override void didUpdateWidget(MyWidget oldWidget) { super.didUpdateWidget(oldWidget); // 当父 Widget 重建时调用。 // 如果父 Widget 重建时需要重新配置子 Widget,则会调用此方法。 } @override void deactivate() { // 在此处理 State 对象从树中被移除的操作 super.deactivate(); } @override void activate() { // 在此处理 State 对象被重新插入树里 super.activate(); } @override void dispose() { // 当 State 对象被永久从树中移除时调用 super.dispose(); } } // 打开页面执行:initState、didChangeDependencies // 关闭页面执行:deactivate、dispose
StatelessWidget 的生命周期方法
dart
体验AI代码助手
代码解读
复制代码
class MyWidget extends StatefulWidget { @override Widget build(BuildContext context) { return Widget; } }
setState()执行做了什么事?
setState()过程主要工作是记录所有的脏元素,会引起build函数执行,更新widget树、更新Element树和RenderObject树,最后重新渲染。
flutter中的key?
- 作用:比较两个Widget是不是同一个Widget
- 分类:LocaleKey、GlobalKey
- LocaleKey:ValueKey、ObjectKey、UniqueKey。
statelesswidget和statefullwidget有什么区别?
StatelessWidget 没有要管理的内部状态.
无状态widget的build方法通常只会在以下三种情况调用:
- 将widget插入树中时
- 当widget的父级更改其配置时
- 当它依赖的InheritedWidget发生变化时
StatefullWidget是可变状态的widget。 使用setState方法管理StatefulWidget的状态的改变。调用setState告诉Flutter框架,某个状态发生了变化,Flutter会重新运行build方法,以便应用程序可以应用最新状态。
在有状态类中编写一个按钮调用初始化生命周期(initState)方法,会发生什么?
报错,但不影响布局;会报生命周期创建错误;
如何获取控件的大小和位置?
- 使用Key拿到上下文取得findRenderObject拿内容的尺寸数据;
- 使用context取得findRenderObject拿内容的尺寸数据;
Flutter 是如何与原生Android、iOS进行通信的?
PlatformChannel
BasicMessageChannel :用于传递字符串和半结构化的信息。
MethodChannel :用于传递方法调用(method invocation)。
EventChannel : 用于数据流(event streams)的通信。
dart
体验AI代码助手
代码解读
复制代码
// 1. 创建java类。 实现FlutterPlugin和MethodCallHandler public class MsaOaidPlugin implements FlutterPlugin, MethodCallHandler{ @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { Log.e("---------","==========onAttachedToEngine"); channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "msa_oaid"); channel.setMethodCallHandler(this); this.context = flutterPluginBinding.getApplicationContext(); System.loadLibrary("msaoaidsec"); } @Override public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { Log.e("---------","==========onMethodCall"); if(call.method.equals("isSupport")){ new DemoHelper().getDeviceIds(context, new IIdentifierListener() { @Override public void onSupport(IdSupplier idSupplier) { result.success(idSupplier.isSupported()); } }); }else if(call.method.equals("getOaid")){ new DemoHelper().getDeviceIds(context, new IIdentifierListener() { @Override public void onSupport(IdSupplier idSupplier) { result.success(idSupplier.getOAID()); } }); }else{ result.notImplemented(); } } @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { channel.setMethodCallHandler(null); Log.e("---------","==========onDetachedFromEngine"); } } // 2. flutter lib包下创建dart类 class MsaOaid { static const MethodChannel _channel = MethodChannel('msa_oaid'); static Future<bool> isSupport() async { final bool support = await _channel.invokeMethod('isSupport'); return support; } static Future<String?> getOaid() async { final String? oaid = await _channel.invokeMethod('getOaid'); return oaid; } }
更多推荐

所有评论(0)