Interview flutter
Flutter + Dart
-
请简单介绍下Flutter框架,以及它的优缺点?
- Flutter 是 Google 开发的 跨平台 UI 框架,用于构建 iOS、Android、Web、Windows、macOS、Linux 甚至 嵌入式设备 应用。
- Flutter 基于 Dart 语言,并使用 Skia 引擎 渲染 UI,具有 高性能、跨平台一致的体验。
- Flutter 适用于
- 跨平台移动开发(iOS + Android)(如微信小程序、B 站客户端)
- 性能要求高的应用(如游戏、动画丰富的应用)
- 需要桌面/Web 兼容的应用
- React Native 适用于
- 前端开发者(熟悉 React 生态,快速开发)
- 已有 React Web 项目的移动端扩展
- 轻量级 App,UI 依赖原生风格
优点 说明 真正的跨平台 一套代码运行在 iOS、Android、Web、桌面 高性能 直接使用 Skia 渲染引擎,无桥接层,接近原生性能 流畅动画 默认 60fps,最高 120fps,动画流畅 热重载(Hot Reload) 代码改动后 秒级生效,大大提高开发效率 原生控件风格 支持 Material(Android) 和 Cupertino(iOS) 风格 强大的 UI 组件 提供丰富的 Widget,无需调用原生组件 更少的桥接 与原生交互(如相机、地图)无需桥接 Java/Kotlin 或 Swift/Objective-C -
Flutter vs React Native
对比项 Flutter React Native (RN) 编程语言 Dart JavaScript (React) 渲染方式 自绘 UI(Skia 引擎) 依赖 原生控件 性能 ✅ 接近原生(更快) ⚠️ 依赖 JS Bridge,稍慢 开发效率 🔥 Hot Reload(秒级重载) 🔥 Fast Refresh UI 组件 所有 UI 组件自绘,跨平台一致 需要 依赖原生控件,部分组件差异大 原生桥接 ✅ 几乎不需要桥接 ❌ 需要桥接 JS <-> 原生 生态 Dart 生态较小 JavaScript 生态庞大 适用场景 跨平台、流畅动画、高性能 App 适用于 Web + 移动端的 React 项目 -
介绍下Flutter的理念架构 Flutter 的设计理念是 “Everything is a Widget”(一切皆组件),它基于 声明式 UI 和 自绘 UI,通过 Skia 渲染引擎 提供跨平台高性能体验。 Flutter 的核心设计目标 跨平台一致性:一次编码,可在 iOS、Android、Web、桌面端运行。 高性能:使用 Skia 2D 渲染引擎,避免原生桥接,接近原生性能。 响应式编程:采用 声明式 UI,通过 setState() 和 Provider 等方式实现 数据驱动 UI。 组件化开发:一切皆 Widget,开发者可以自由组合和扩展 UI 组件。 Flutter 三层架构 Framework(Flutter 框架层) 提供 Widget、动画、手势、绘制、调度 等核心功能(Dart 代码) Engine(Flutter 引擎层) 使用 Skia 进行 2D 渲染,Dart VM 运行 Dart 代码(C++) Embedder(平台适配层) 适配不同平台(Android/iOS/Web/桌面),调用原生 API(Java/Kotlin/Swift/Objective-C)
-
项目数据库版本升级方案是怎么做的
- 如果你的 Flutter 应用使用 sqflite 作为本地数据库存储方案,数据库升级通常通过 onUpgrade 回调方法进行管理。
- 增加 version 号(例如,从 1 变为 2)。
- 在 onUpgrade 方法中实现升级逻辑,例如:
- ALTER TABLE 语句添加新字段。
- CREATE TABLE 语句新增表。
- 迁移旧数据。
-
路由有哪些
- 基本路由
- Navigator.push / Navigator.pop
- 命名路由(Named Route)
- Flutter支持命名路由,在MaterialApp或CupertinoApp的routes参数中定义
- 为什么选择GetX路由?
- 更简洁:不需要context,代码更少。
- 支持命名路由:避免手动管理路由栈。
- 支持中间件:可以进行路由拦截。
- 支持动画:自定义跳转动画。
- 全局管理:结合GetX的状态管理,能高效管理应用。
- 基本路由
-
你在项目中常用的第三方插件有哪些?
- provider:Flutter官方推荐的状态管理方案,轻量且易用。
- get:轻量级、简洁的Flutter状态管理和路由管理插件。
- dio:功能强大的网络请求库,支持拦截器、队列、表单上传等。
- shared_preferences:适用于存储少量的键值对数据,如用户配置。
- hive:轻量级、高性能的NoSQL数据库,适用于本地存储。
- fluttertoast:Flutter的toast提示插件。
- flutter_svg:支持SVG图片格式的插件。
- qr_flutter:生成二维码插件。
- flutter_screenutil:屏幕适配方案,用于调整屏幕和字体大小的flutter插件。
- cached_network_image:它能够实现图片缓存、错误处理和占位符,提高性能和用户体验。
- 图片缓存:避免重复下载,提高性能
- 支持占位符:在图片加载时显示进度条或默认图
- 支持错误处理:网络失败时可显示错误图标
- 性能优化:减少网络请求,提高流畅度
-
Flutter中设置圆角图片的方式
- ClipRRect:适用于大多数情况,简单直接,支持网络、本地和资源图片。
- Container + BoxDecoration:适用于更复杂的UI,如需要同时设置背景、边框、阴影等。
- CircleAvatar:适用于圆形头像。
- ClipOval:将图片裁剪为椭圆形。
- ShaderMask:可实现更高级的圆角效果,可以结合渐变色、透明度等更复杂的图像效果。
-
Dart 的作用域
- 局部变量,函数内可见。
- 形式参数,函数内可见。
- 全局变量,程序中可见。
- 词法闭包,只要调用就可见。
-
Dart 当中的 「..」「.」分别表示什么意思?
- Dart 当中的 「. .」意思是 「级联操作符」,为了方便配置而使用。「. .」和「.」不同的是 调用「. .」后返回的相当于是 this,而「.」返回的则是该方法返回的值 。
-
Dart 当中的 「…」表示什么意思?
- 扩展运算符,用于扩展数组。
-
Dart 是不是单线程模型?是如何运行的?
- Dart 采用单线程事件循环模型,但支持异步编程(异步事件队列 + Future + async/await + Isolate),可以实现并发操作。
- Dart 采用 事件驱动 + 任务队列 模型。它的主线程只有一个,所有的任务都会被放入不同的队列中执行:
- 同步任务(Synchronous Task):立即执行,放在主线程执行栈。
- 微任务队列(Microtask Queue):优先级高,执行所有微任务后才会执行事件队列任务。
- 事件任务队列(Event Queue):例如 Future.delayed()、Timer、I/O 任务等。
- Dart 的任务执行顺序:
- 执行所有同步任务(main 函数中所有同步代码)
- 执行微任务队列中的所有任务
- 执行事件队列中的第一个事件任务
- 返回步骤 2,继续执行微任务队列
- 重复执行
-
Dart的任务队列
- 微任务队列优先:scheduleMicrotask() 任务优先执行。
- 事件队列:Future、I/O、Timer 等异步任务放入事件队列。
-
Dart种类型有哪些?
- Dart 是 强类型(Strongly Typed) 语言,但同时支持 类型推断。Dart 的类型主要分为以下几类:
- 数值类型(Numbers)
- int:整数类型
- double:浮点数类型
- num:可以是 int 或 double
- 字符串(String)
- String:字符串类型,支持 + 拼接、多行字符串、字符串插值 ${}
- 布尔类型(Boolean)
- bool:只有 true 和 false 两个值
- 在 Dart 中,只有 true 被视为真,其他值(如 null)不会自动转换为 false。
- 集合类型
- Dart 提供三种主要的集合类型:
- List(列表)
- Set(集合)
- Map(字典)
- dynamic:
- dynamic 允许变量存储任何类型,并且可以在运行时更改类型。
- 注意:避免滥用 dynamic,会降低 Dart 的类型安全性。
- var(类型推断):
- var 由编译器自动推断类型,但不能更改类型:
- Object(所有类型的父类)
- Object 是 Dart 中所有类型的基类。
- 区别:dynamic 允许修改类型,而 Object 只允许存储,但不能更改类型。
- Null:
- Null 代表 null 值,用于空安全处理。
- Future(表示一个异步任务):
- Future 代表未来可能完成的值,可以使用 await 等待它完成
- Stream(用于处理多个异步数据)
- Stream 适用于 连续的数据流,比如 WebSocket 连接。
-
什么是Isolate
- Isolate(隔离线程)
- Dart 的单线程模式有一个例外:Isolate(隔离线程),它是 Dart 用于多线程并发编程的机制,类似于 JavaScript 的 Web Workers。
- Dart 不支持共享内存的多线程,Isolate 之间相互独立,只能通过 消息传递 通信(SendPort / ReceivePort)。
- 适用于 CPU 密集型计算(如图像处理、数据计算等)。
- 但通过异步任务和 Isolate 提供并发能力,保证 UI 流畅性!
- Isolate(隔离线程)
-
dynamic和Object的区别
- dynamic 和 Object 都可以用于存储任何类型的值,但它们有一些关键区别
- dynamic 适用于动态数据,可以随意调用方法,但可能导致运行时错误。
- Object 适用于泛型存储,必须先转换类型,更安全但更严格。
-
Dart是如何实现多任务并行的?
- 单线程异步
- Dart 的 Future 用于执行异步任务,适用于 网络请求、数据库查询、文件操作等。
- 更优雅的处理方法(async/await)
- Stream(异步数据流)
- Isolate
- Dart 默认是单线程的,但 Isolate 允许创建真正的并行任务,适用于 CPU 密集型计算(如数据加密、图像处理等)。
- Dart 运行在 单线程 中,Future 和 Stream 只是异步,不是真正的并行。
- Isolate 是 独立的线程,不能共享内存,只能通过 消息传递 通信。
- compute()(Flutter 提供的简化 Isolate 方法)
- 在 Flutter 中,compute() 用于执行耗时任务,它会自动创建 Isolate 处理任务。
- Timer
- Timer(Duration, callback) 延迟执行。
- 适用于 倒计时、动画定时器等。
- 单线程异步
-
stream有哪两种订阅模式的区别
- Stream 提供了两种订阅模式:单订阅和广播
- 单订阅(Single Subscription)Stream
- 默认模式(Stream 默认是单订阅的)。
- 只能有一个监听器(listen() 只能调用一次)。
- 适用于一次性数据流(如文件读取、HTTP 请求返回的流)。
- 不能多次订阅,否则会报错。
- 文件读取、网络请求等
- 广播(Broadcast)Stream
- 允许多个监听器(多个 listen() 订阅同一个流)。
- 适用于广播事件(如 UI 事件、消息总线、WebSocket)。
- 不会存储历史数据,只会向当前订阅者发送数据。
- UI 事件、WebSocket、全局消息总线
-
StreamController vs. Stream 的区别
- 什么是 Stream?
- Stream 是一个异步数据流,可以不断发送数据,并让订阅者 listen() 监听数据。
- Stream 本身不能添加数据,它只是数据流的 消费者。
- 什么是 StreamController?
- StreamController 可以主动管理数据流,它允许:
- 手动添加数据:controller.sink.add(value);
- 控制流的生命周期:close() 关闭流
- 支持广播模式:StreamController.broadcast()
- 什么是 Stream?
-
await for 如何使用?
- await for 是 Dart 用于异步 Stream 监听 的关键字,类似于 for 循环,但它用于 逐个等待 Stream 的数据。
- await for 只能用于 async 方法。
- 适用于 Stream 类型(不能用于 Future)。
- 每次等待 Stream 发送的数据,并在循环体中处理数据。
-
说一下 mixin机制?
- Dart 的 mixin 机制允许 在多个类之间共享代码,类似于多继承,但更灵活。
- mixin 主要用于复用方法,而不需要继承(extends)。
- mixin 继承(on 关键字): 限制只能在某些类中使用
- 复用方法(日志、权限、动画)用 mixin
- 需要存储数据或定义默认行为,用 abstract class
- 避免 mixin 滥用,合理限制 on 关键字
-
Dart - extends, Mixin, implements
- extends(继承 - 只能继承一个类)
- mixin(代码复用 - 可以多个)
- implements(接口 - 必须实现所有方法)
-
介绍下Widget、State、BuildContext 概念
- Widget 是 Flutter UI 的基本构建单元,一切皆 Widget!
- Flutter 的 UI 是由 嵌套的 Widget 树 组成,每个 Widget 代表界面上的一个元素,例如文本、按钮、容器等。
- 无状态 Widget (StatelessWidget) :不可变(创建后不会变化)Text、Icon、Container
- 有状态 Widget (StatefulWidget) :可变(支持更新 UI)TextField、ListView
- State:
- 创建:initState() 初始化状态(只执行一次)
- 构建UI:build() 返回 UI 组件
- 状态更新:setState() 触发 UI 重建
- 销毁:dispose() 释放资源(如动画、控制器)
- BuildContext 是 Widget 在 Widget 树中的位置,用于访问父级 Widget 或导航等操作。
-
StatefulWidget 的生命周期
- initState() 只执行一次,适合初始化操作
- setState() 触发 build() 更新 UI
- dispose() 释放资源,防止内存泄漏
- didChangeDependencies() 当 InheritedWidget 发生变化时调用
- didUpdateWidget() 当 StatefulWidget 被更新时调用
- deactivate() 组件被移除时调用,但可能重新插入
-
InheritedWidget 是什么
- Flutter 内置的数据共享机制,适用于全局数据传递
- updateShouldNotify() 决定是否通知子组件重建
- of(BuildContext context) 获取最近的 InheritedWidget
-
简述Widgets、RenderObjects和Elements的关系
- Widget Tree用于整体的布局的配置
- Element Tree负责Widget的生命周期,管理父子关系,更新UI
- RenderObject Tree负责确定大小并渲染
-
什么是状态管理,你了解哪些状态管理框架?
- 在 Flutter 中,UI 由 Widget 树 组成,每次 setState() 更新时,Flutter 会 重建 Widget,但在复杂应用中,多个组件之间的数据共享 变得困难,因此需要状态管理。
- InheritedWidget
- Flutter 原生状态管理,适用于 简单的全局状态,如 Theme.of(context)。
- Provider(官方推荐)
- 基于 InheritedWidget,更易用,Flutter 官方推荐。
- GetX
- 简单,适用于快速开发
- 内置路由管理
- 无 context 限制,适合独立组件
-
简述Flutter的绘制流程
- Widgets (构建) → Elements (管理) → RenderObjects (渲染)
- Widgets(构建 UI)
- ↓
- Elements(管理 UI)
- ↓
- RenderObjects(布局 & 绘制)
- ↓
- Compositing(合成)
- ↓
- Rasterization(GPU 处理)
-
简述Flutter的线程管理模型
- Platform Task Runner:与平台的主线程相对应,用于处理与平台的交互;
- UI Task Runner:用于执行 Dart 代码,处理 UI 渲染和帧更新(Root Isolate就是运行在这里)。
- GPU Task Runner:用于执行 GPU 绘制任务。
- IO Task Runner:用于执行输入/输出任务,如文件读写、网络操作等;
-
Flutter 是如何与原生Android、iOS进行通信的?
- MethodChannel 最常用,单次请求-响应模式 调用原生 API(如相机、GPS)
- EventChannel 数据流模式,持续通信 监听传感器、蓝牙、GPS 等数据
- BasicMessageChannel 双向通信,支持复杂对象 复杂数据传输(JSON、二进制)
- FFI(Foreign Function Interface) 直接调用 C/C++ 代码 高性能计算(如视频处理)
-
简述Flutter的热重载
- Flutter 之所以能支持 热重载(Hot Reload),是因为 Dart VM(虚拟机)支持 JIT(Just-In-Time)编译,允许 Flutter 在 不重新启动整个应用的情况下更新代码,从而实现 快速 UI 预览和调试。
- Flutter 热重载的关键是 增量代码更新 + 重新构建 UI:
- 开发者修改 Dart 代码
- Flutter 通过 Dart VM 运行增量更新
- 更新 Widget 树,不重启应用
- UI 组件保留当前状态(State 不变)
-
Flutter里的key有什么用?
- Element负责调起canUpdate会比较runtimeType和key两个对象,如果不指定key则只会对比runtimeType,而当同一个层级下的有状态组件位置发生变化,element会认为不需要更新而是复用旧的组件的state。
-
setState的过程是什么
- 调用 setState() 更新状态
- Flutter 标记 Widget 需要重建
- 触发 build() 方法
- Element 树对比新旧 Widget 树(diff 计算)
- 复用未变更的 Widget,更新变更部分
- 触发 RenderObject 重新布局 & 绘制
-
Flutter刷新页面的方法有哪些?
- setState():StatefulWidget 局部更新
- StreamBuilder(基于流的刷新):数据流更新(如 WebSocket、传感器数据)
- FutureBuilder(基于 Future 的刷新):网络请求、数据库查询
- ValueNotifier + ValueListenableBuilder(轻量级刷新):局部状态更新,而不影响整个 Widget 树
- ChangeNotifier + Provider:基于状态管理刷新
-
flutter里有几个Key,它们的作用是什么?
- LocalKey(局部唯一 Key)
- LocalKey 只在同一个 Widget 树分支内唯一,用于优化 列表、动画组件 的复用。
- GlobalKey(全局唯一 Key)
- GlobalKey 用于 在整个 Widget 树中唯一标识组件,可以跨 Widget 树访问 State、BuildContext。
- LocalKey(局部唯一 Key)
-
global key他怎么是怎么拿到那个视图的长宽呢?组件的长宽怎么拿到?
- 创建 GlobalKey 并赋值给 Widget
- 通过 globalKey.currentContext 获取 BuildContext
- 通过 context.findRenderObject() 获取 RenderBox
- 调用 RenderBox.size 获取 Size(宽高)
-
InheritedWidget的作用是什么?
- InheritedWidget 是 Flutter 提供的 跨组件状态共享机制,用于在 Widget 树中向子组件传递数据,避免 setState() 逐层传递,提高性能和代码简洁性。
- Theme.of(context)、MediaQuery.of(context) 都是 InheritedWidget
-
有一个TextField的跟随软键盘一直往上移啊,这个该怎么做呢?
- 在TextFiled外面加Padding
- MediaQuery.of(context).viewInsets.bottom是键盘弹起时、获取到的键盘高度
- 在输入框下方填充额外内容,来使输入框可见
-
Flutter动画怎么做?
- TweenAnimationBuilder(自定义动画数值)
- AnimatedContainer,AnimatedPadding之类的动画组件
- CustomPainter自定义画布动画
- AnimatedBuilder控制动画进度,避免 setState() 重建整个 Widget
- 总结:
- 尽量使用 AnimatedContainer、TweenAnimationBuilder 处理简单动画
- 对于复杂动画,使用 AnimationController + AnimatedBuilder
- Hero 适用于页面切换
- CustomPainter 适用于复杂绘制动画
-
Sliver系列的组件用过吗?
- SliverAppBar 滚动折叠的 AppBar
- SliverList 滚动列表(类似 ListView)
- SliverGrid 滚动网格(类似 GridView)
- SliverPersistentHeader 固定/吸顶 Header
- SliverToBoxAdapter 适配普通 Widget
- SliverPadding 给 Sliver 组件加内边距
- SliverFillRemaining 填充剩余空间
-
普通的ListView组件和Sliver系列的组件有什么区别?
- 适用于复杂滚动页面
- 支持多个滚动组件组合
- 可以与 SliverAppBar、SliverGrid 混合使用
-
StatelessWidget 和 StatefulWidget 的区别?
- StatelessWidget(无状态组件)
- 组件的数据在创建后不会改变。
- 适用于静态 UI,例如 Text、Icon
- StatefulWidget(有状态组件)
- 组件的数据会随着用户交互而变化。
- 适用于需要更新 UI 的场景,例如按钮、输入框、动画等。
- StatelessWidget(无状态组件)
-
我用getX去改变一个组件的颜色我应该怎么做呢?
- 使用 Rx 响应式变量(推荐)
- 使用 GetBuilder和Update(手动触发 UI 更新)
-
有个pageView,pageView可以左右滑动,,它会重新走上生命周期。我现在不想走生命周期,就是想保存当前状态,该怎么做?
- AutomaticKeepAliveClientMixin 是 Flutter 提供的 用于在 PageView、ListView 等滚动视图中保持页面(State)状态 的 Mixin。
- Flutter 的 PageView 或 ListView 会在滑出屏幕后销毁不可见页面的 State,导致:
- 切换回来时,页面重新 build(),状态丢失。
- 比如:输入框内容消失、列表滚动到顶部。
-
你们有没有先送送礼物啊,直播间送礼物,把SVG礼物在那里面播放用
- vap一种视频格式
-
那有做过flutter的优化吗?
- 避免不必要的 setState()
- setState() 会导致整个 build() 方法重新执行,可能会影响不需要更新的组件。
- 使用 ValueNotifier 代替 setState()。
- 使用状态管理的组件优化更新的粒度
- 使用 const 构造 Widget
- 每次 build(),Flutter 可能会创建新的 Widget,即使它们没有变化。
- 使用 const 关键字,使 Widget 在内存中只创建一次:
- 使用 ListView.builder() 代替 ListView
- ListView 会一次性渲染所有列表项,导致性能问题。
- 使用 ListView.builder() 按需加载
- 使用 RepaintBoundary 限制重绘区域
- Flutter 默认会重绘整个 Widget 树,可能会导致不必要的性能损耗。
- 使用 RepaintBoundary 限制重绘范围
- 优化图片加载
- 使用 cached_network_image 进行图片缓存
- Flutter 运行在单线程上,CPU 任务(如 JSON 解析)可能会阻塞 UI。
- 使用 compute() 进行多线程处理
- 避免不必要的 setState()