自我介绍:
你好面试官,我叫xx, 做flutter开发大概两年的时间,上一家在浙江xx工作,工作内容主要是开发公司业务相关的Flutter App。
看了贵公司的职位,我有三个优势:
第一是我经历过项目从0到1的过程,具备项目基础搭建的经验,无论是日常的项目迭代、还是技术实现方案,都深入参与,具体的项目我放到了简历中。
第二是我熟悉并深入参与国际化相关的业务。熟悉国际化业务的常见问题和解决方案。
第三是除了正常的业务开发,多次深入参与全面升级优化改版。
我看到了岗位的要求信息,我有信心能满足该岗位的需求,希望这次面试能给你一个满意的答复。
Flutter常问的一些基础问题
Dart语法的常见面试问题
const和final
在 Dart 中,const 和 final 都用于定义不可变的变量,但它们的本质区别在于——
final是运行时常量,值在程序运行时被初始化;const是编译时常量,值必须在编译阶段就能确定。
此外,const 创建的是完全不可变对象,连对象内部的字段也不能变,而 final 修饰的对象虽然引用不能变,但如果是可变对象,它的内容仍然可以改变:
在 Flutter 中,我常用 const 来标记不会变化的 Widget,能减少重建次数,提高性能;而 final 更多用于接收初始化时的配置或外部传参值,比如类的构造参数。
值传递还是引用传递
Dart 是 纯粹的值传递语言(pass-by-value) 。这意味着无论传递什么类型,传递的都是“值的副本”。
对于基本类型(如 int、double、bool、String 等不可变类型),传递的是值的副本,函数内部修改参数不会影响外部变量。
对于对象类型(如 List、Map、自定义类),传递的是“引用的值的副本”。也就是说,函数接收到的是原始对象的引用副本,可以修改对象内部的内容,但不能改变原始变量的引用指向。
StatefulWidget和StatelessWidget区别
Statelesswidget是静态的,状态在创建后不可变,一般写一些静态,不需要更新的组件。
Stateful状态可以改变,setState,
Widget生命周期
· createState:创建 State 对象。
· initState:初始化状态,在 Widget 树构建前调用。
· didChangeDependencies:当 State 对象的依赖项发生变化时调用(例如,InheritedWidget 的变化)。通常在 initState 后第一次调用。
· build:构建 Widget 树,渲染 UI。
· didUpdateWidget:当父 Widget 重建时,传递新的参数给当前 Widget,检查是否需要更新状态。
· setState:通过调用 setState 来更新 Widget 的状态,触发 UI 重新构建。
· dispose:销毁 Widget,清理资源
三棵树的关系, Widget 和 element 和 RenderObject
- Widget:UI的配置单元
- Element:Element是实例化的 Widget 对象,通过 Widget 的 createElement() 方法,是在特定位置使用 Widget配置数据生成;
- RenderObject:用于应用界面的布局和绘制,保存了元素的大小,布局等信息;
在 Flutter 中,UI 的构建和渲染涉及三棵核心的树结构:Widget Tree、Element Tree 和 Render Tree(RenderObject Tree) 。它们分别承担了描述、连接、渲染的职责。
- Widget Tree — 描述阶段(immutable,声明式)
- Widget 是UI 的配置描述,是不可变的对象(immutable)。
- 它描述了界面应该长什么样,但不包含任何实际的渲染逻辑或状态。
- 每次调用
build(),就会创建新的 Widget 树。
- Element Tree — 实例阶段(桥梁,状态保持)
- Element 是 Widget 的一个“实例”,负责连接 Widget 和 RenderObject。
- 它是树中真正持久存在的部分,负责维护 Widget 的生命周期、状态缓存和 diff 对比。
- 每次 Widget 重建时,Flutter 会用新的 Widget 与旧的 Element 比较,决定是否需要更新。
StatelessElement和StatefulElement就是分别对应StatelessWidget和StatefulWidget的 Element 类型。
- Render Tree — 渲染阶段(真正绘制 UI)
- RenderObject 是用来布局、测量、绘制、命中测试的。
- 是 Flutter 的底层渲染单位,比如
RenderBox。 RenderObject由 Element 创建和管理,一般由RenderObjectWidget(如RenderBoxWidget)间接生成。
Widget 是 UI 配置;
Element 是 Widget 的实例和状态管理者;
RenderObject 是底层真正的绘制实体。
三者形成了声明式 UI 到高性能渲染之间的完整链条,是 Flutter 高性能的核心。
那么,flutter为什么要设计成这样呢?为什么要弄成复杂的三层结构?
答案是性能优化。如果每一点细微的操作就去完全重绘一遍UI,将带来极大的性能开销。flutter的三棵树型模式设计可以有效地带来性能提升。
widget的重建开销非常小,所以可以随意的重建,因为它不一会导致页面重绘,并且它也不一定会常常变化。 而renderObject如果频繁创建和销毁成本就很高了,对性能的影响比较大,因此它会缓存所有页面元素,只是当这些元素有变化时才去重绘页面。
而判断页面有无变化就依靠element了,每次widget变化时element会比较前后两个widget,只有当某一个位置的Widget和新Widget不一致,才会重新创建Element和widget;其他时候则只会修改renderObject的配置而不会进行耗费性能的RenderObject的实例化工作了。
flutter状态管理
单个页面内:直接setState;
InheritedWidget(Flutter 内建):
- 是 Flutter 原生提供的跨组件通信机制。
- 高效,但写法复杂、维护性差,通常用于封装底层依赖注入。
Provider(官方推荐):
- 封装了 InheritedWidget,API 简洁,性能好。
- 支持状态响应、依赖注入、懒加载等功能。
- 更新的颗粒度不够、范围太大,consumer包住的东西都要状态更新。不能像changeNotifer那样更新某个数据。
Riverpod(推荐):
- Provider 的升级版,支持纯函数、类型安全、无上下文依赖。
- 状态管理 + 依赖注入二合一,逻辑清晰,测试友好。
Bloc / Cubit:
- 强约束的状态管理框架,适合大型、复杂逻辑应用。
- 基于事件驱动(Event → State),适合企业级项目。
Get:
- 一体化框架(状态管理、路由、DI),轻量快速。
- 写法简单,适合个人项目,但可维护性相对较弱。
- 缺点, github 有1k+ issues
一个页面要想刷新一个页面,这个可以通过路由回调,超过两个页面,直接eventBus。
Flutter异步操作
Dart 是单线程模型,通过 事件队列(Event Queue)+ 微任务队列(Microtask Queue) 实现异步任务调度。
Dart 的事件循环由两个主要队列组成:
-
微任务队列(Microtask Queue)
- 优先级高
- 由
scheduleMicrotask()或Future((){})产生
-
事件队列(Event Queue / Message Queue)
- 优先级低
- 包含
Future.delayed()、Timer、I/O、UI 事件等
事件循环机制会:
- 不断先清空微任务队列
- 然后执行事件队列中的第一个任务
- 然后再清空新加入的微任务……
Stream 与 Future是什么关系?
1、Future 表示稍后获得的一个数据,所有异步的操作的返回值都用 Future 来表示。
2、Future 只能表示一次异步获得的数据。
3、Stream 表示多次异步获得的数据。比如界面上的按钮可能会被用户点击多次,按钮上的点击事件(onClick)就是一个 Stream 。
4、Future将返回一个值,而Stream将返回多次值。
5、Dart 中统一使用 Stream 处理异步事件流。
Flutter和原生通信
Flutter通过Platform Channels机制实现与iOS(Swift/OC)和Android(Kotlin/Java)原生代码通信。
基本原理是基于异步消息传递:Flutter端发送消息到原生端,原生端收到后执行对应操作,再异步返回结果。
支持多种消息编解码格式,默认是StandardMessageCodec。
Flutter和原生以Method Channel、Event Channel和Basic Message Channel三种方式通信:
MethodChannel:Flutter调用原生方法,等待返回结果,适合请求-响应模式。
EventChannel:原生主动发送事件流给Flutter,适合传输设备状态或传感器数据等持续推送。
BasicMessageChannel: 双向数据流,传递自定义消息,适合复杂的交互。
MethodChannel:用于传递方法调用(method invocation)通常用来调用 native 中某个方法
BasicMessageChannel:用于传递字符串和半结构化的信息,这个用的比较少
EventChannel:用于数据流(event streams)的通信。有监听功能,比如电量变化之后直接推送数据给flutter端
三种 Channel 之间互相独立,各有用途,但它们在设计上却非常相近。每种 Channel 均有三个重要成员变量: name: String类型,代表 Channel 的名字,也是其唯一标识符
messager:BinaryMessenger 类型,代表消息信使,是消息的发送与接收的工具
codec: MessageCodec 类型或 MethodCodec 类型,代表消息的编解码器
wifi通信
-
Wi-Fi:基于 TCP/IP,通信范围大、速度快,适合局域网通信、大数据传输。
-
蓝牙(BLE) :低功耗、短距离、基于 GATT 协议,适合 IoT 设备配对、控制和状态获取。
之前我做过的项目里,光伏储能行业里,有通信设备和采集器,通过wifi协议和设备交互, AR眼镜项目,是通过蓝牙和AR眼镜交互的。
-
通常是通过 TCP/UDP 进行局域网通信;
-
可使用
network_info_plus获取当前 Wi-Fi 信息; -
使用
dart:io.Socket实现 TCP 客户端连接设备; -
可通过
udp插件广播设备发现。
Wifi通信流程
- 获取当前 Wi-Fi 信息
-
使用插件:
network_info_plus- 获取 IP、SSID、BSSID(设备唯一标识)
- 判断是否连接特定热点(用于设备绑定)
- 控制 Wi-Fi 连接(连接某个热点)
-
Flutter 无法直接切换 Wi-Fi 热点,但可以通过原生插件或平台通道实现:
- Android 可使用
wifi_iot - iOS 受系统限制(只能跳转设置页)
- Android 可使用
- Wi-Fi 通信(重点)
- 使用
dart:io.Socket实现 TCP 连接设备 - 使用
udp插件广播或监听局域网数据
Protobuf 协议
flutter中有Protobuf 协议,高效、轻量级、跨平台、跨语言的序列化协议。
它将结构化数据编码成紧凑的二进制格式,用于网络通信、数据存储等场景,比 JSON 更小更快。
之前采用这种数据格式和通信棒和采集器交互。
蓝牙通信
Flutter_blue_plus 三方库。
蓝牙本来有经典蓝牙和低功耗蓝牙,游戏手柄、耳机这种一般是经典蓝牙,
权限申请(分析不同平台差异,如Android需动态申请定位/蓝牙权限,iOS需在Info.plist声明)
设备扫描
过滤设备(如根据name或service UUID),降低误连概率
连接与断线重连
异步连接,结合超时/自动重连逻辑
服务&特征值发现
Discover并缓存对应特征,避免重复发现损耗
读写通信/监听通知
写命令、读数据、接收notify(如传感器推送数据)
1.国际化问题
语言国际化实现方案
目前flutter实现国际化的三方框架有intl和get,都可以实现国际化,在实际的项目中,我其实都有用到,最初是用intl实现国际化,后来因为get是一套工具框架,为了接入get生态,把intl换成了get。
Intl本身是配置arb文件,arb文件和json差不多。实际项目中用到的时候,发现一个缺点是,没法像json一样一个键值对嵌套着一个键值对声明。
我们项目里对每个小语种,包括英语、西班牙语、法语、、意大利语、日本语等小语种国际化文本用json配置,然后用脚本转换成get的控制器类。
这里涉及到一个国际化文本的翻译问题,首先项目中有接入三方框架deepl翻译来自动机翻,这个翻译有时没有那么专业化,有些词汇表需要和产品部的同事沟通确认,同时还要拉通其他端比如web、后端来确保翻译一致。
还涉及到一个问题是国际化界面布局,小语种一般都比较长,有些布局拓展性不好会显示异常,UI进行页面设计、开发页面布局的编码时需要注意小语种太长的情况,确保界面能够兼容中文和其他超长的小语种文本。
常见的业务问题
国际化业务
首先需要确定App是通过发不同的app包为不同国家地区提供服务,还是一个app用户通过进入app后选择国家地区站点的方式。
如果是抖音app这种大陆发一个、香港发一个app包这种,如果很多业务使用同一套代码,可以用flutter的flavor来弄。
Flutter 中的 flavor 本质上是通过配置不同的构建设置来创建应用的多种版本。比如开发环境的包、测试环境的包、国内生产环境的包、国外生产环境的包。
如果是一个app用户通过进入app后选择国家地区站点的方式。需要在具体的业务代码里,面对不同国家不同地区的做具体的判断。
2.扫一扫怎么做的****
扫一扫可以分成两个部分,扫描二维码和生成二维码
扫描二维码分成多种情况,扫描光伏设备上的二维码和扫描云平台生成的电站二维码、个人信息二维码。
扫一扫的组件是基于mobile_scanner 三方库 二次封装的,业务中如果是扫描设备的二维码,设备的二维码的内容是设备的SN,扫描出结果后可以进行添加光伏逆变器设备的业务操作。
如果是平台生成的电站信息二维码或者个人信息二维码,扫出二维码的结果后,走解密流程得到二维码的信息,去走对应的电站添加设备业务。
3.三方库二次封装搭建****
网络请求库:DIO、
地图组件:location、geolocator三方库用来获取当前位置,国内使用百度地图、国外使用谷歌地图、商用地图授权。
json_serializable: 简化 JSON 数据与 Dart 对象之间的转换过程。自动生成序列化/反序列化代码
get是一套工具的的,包括路由、页面的脚手架,国际化语言、状态管理,都是用这个弄的
eventBus:用来跨页面刷新。
Context是什么****
在flutter中,BuildContext代表widget树中的位置,通过这个东西能找到widget的父控件和祖先控件
key是什么****
Key 是一个用于标识 Widget、Element 和 RenderObject 的对象。它的主要作用是帮助 Flutter 框架在 Widget 树更新时,正确地定位和匹配 Widget 的状态。
常见的设计模式****
单例模式,工厂模式。
还有什么要问的?
(1) 这个岗位的工作重心,工作指责是什么
(2) 公司对这个岗位的期待是什么
(3) 目前团队人员结构和分工是怎样的
(4) 后续面试流程是怎样的
(5) 您能否分享一下您的工作体验是怎样的呢 ,当前您所在的团队或者业务有没有遇到什么问题、挑战、工作重心等,如果我加入需要优先解决什么工作或者问题。
(6) 您希望这个岗位在入职试用期内,半年内、一年分别达成什么样的目标
(7) 您对我这种经验大概一年半的程序员有什么建议,是持续加深打磨技术?更深入业务?
Boss终面反问:****
您的团队风格是什么样的?什么样的候选人可以更好地融入您的团队?
您对这个岗位的预期是什么样的?更看重什么样的人才?
您对我这种年轻人有没有什么建议?
周围人对你的评价是什么?****
领导对我的评价是:可能经验上比不上那种十年开发经验的,但工作的态度是比较积极的,完成工作方面也是比较有责任心的。
同事的评价:平时同事之间主要是友好和善的沟通协作,不会进行互相评价哈。
有没有其他Offer****
我现在有几个公司还在最终面试中,但这次我看机会会慎重一些,还是期望去到一个合适的平台长期发展,但几轮面试聊下来,对贵公司的业务、所处行业已经了解的比较多了,在前几轮的面试过程中,和几位面试官聊的也还是比较好的,还是很期待拿到贵公司的机会。