前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础进阶

  • 精选模块

    • 精选篇

      • 1 HTML模块
      • 2 CSS模块
      • 3 JS模块
      • 4 ES6模块
      • 5 浏览器模块
      • 6 React模块
      • 7 Vue模块
      • 8 Node模块
      • 9 前端工程相关
      • 10 移动多端开发
      • 11 小程序模块
      • 12 Uniapp模块
      • 13 前端安全模块
      • 14 性能优化相关
      • 15 HTTP模块
      • 16 常用设计模式
      • 17 框架通识
      • 18 排序算法
      • 19 计算机通识
      • 20 React Native模块
      • 21 桌面端模块
      • 22 Flutter模块
      • 23 Android模块
      • 24 iOS模块
    • 进阶篇

# Flutter 面试题精选

覆盖 Dart、Flutter Framework、渲染、状态、并发、平台集成、性能、测试、发布、工程实战,以及 Flutter / React Native / uni-app 高频对比。每题均含速记、代码、详细解析和追问;核心流程可逐帧交互。

# 1. 架构与选型

# 1. Flutter 的整体架构分哪几层?各层干什么?

30 秒速记

  • 三层:Framework(Dart)→ Engine(C++)→ Embedder(各平台原生代码),越往下越贴近系统
  • Framework 内部再分:foundation → animation / painting / gestures → rendering → widgets → Material / Cupertino
  • 三棵树:Widget 是配置(轻、不可变、随时重建),Element 是实例和身份(持有 State),RenderObject 管布局和绘制
  • build 被调用 ≠ 重新布局 ≠ 重新绘制,中间有 Element 做比对,只有属性真变了才往下标脏
  • 业务代码几乎只碰 widgets 及以上,自定义布局/绘制才下到 rendering 层

Flutter 自上而下是 Framework、Engine、Embedder 三层,业务写的 Dart 代码全在 Framework 里。 Framework 负责把你的 Widget 变成一棵渲染树,算出布局、生成绘制指令;Engine 是 C++ 写的,带着 Dart 运行时、文字排版和渲染后端(Skia 或 Impeller),把绘制指令真正变成像素;Embedder 是每个平台的壳,负责建窗口、接 vsync、转发触摸和生命周期。面试我会顺带讲三棵树:Widget 只是一份配置,重建很便宜,Element 才是真正挂在树上的身份,RenderObject 干重活,所以「频繁 build」本身不一定是性能问题。

# 先用大白话把三层讲清楚

可以把 Flutter 应用想成一个自带画笔的游戏:Framework 是游戏逻辑,决定画什么;Engine 是画笔和画布,负责真的画出来;Embedder 是游戏机的外壳,管屏幕、按键和开关机。原生 Android 的按钮是系统画的,Flutter 的按钮是 Engine 自己一笔一笔画的,系统只提供一块空白画布。

层 语言 主要内容 业务会不会碰
Framework Dart widgets、rendering、手势、动画、Material / Cupertino 组件库 天天碰
Engine C++ Dart VM、Skia / Impeller、文字排版、platform channel 的底层通道 基本不碰
Embedder Java/Kotlin、ObjC/Swift、C++ 窗口/Surface、线程、vsync、输入事件、插件注册 写插件、混合开发时碰

# 三棵树是 Framework 层的核心

Widget 是不可变的配置对象,每次 setState 都会生成一批新的;Element 是框架根据 Widget 创建的长期实例,它拿新旧 Widget 比较 runtimeType 和 key,一样就复用自己,只把新配置递给下面的 RenderObject;RenderObject 收到新属性后,只有值真的变了才调用 markNeedsLayout 或 markNeedsPaint。所以一帧里 build 可能跑了很多次,但真正重新布局、重新绘制的节点可能很少。

下面这段能直接跑,看清 BuildContext 其实就是 Element,以及同一个 Element 在重建后不变:

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: Counter()));

class Counter extends StatefulWidget {
  const Counter({super.key});
  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int n = 0;

  @override
  Widget build(BuildContext context) {
    // context 就是当前 Element,每次重建打印的 hashCode 不变
    debugPrint('element=${identityHashCode(context)} widget=${identityHashCode(widget)}');
    return Scaffold(
      body: Center(
        child: Builder(builder: (ctx) {
          // 拿到 Text 下面那个 RenderObject(RenderParagraph)
          WidgetsBinding.instance.addPostFrameCallback((_) {
            debugPrint('render=${ctx.findRenderObject().runtimeType}');
          });
          return Text('$n');
        }),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => n++),
        child: const Icon(Icons.add),
      ),
    );
  }
}
// 点几次按钮:element 的 hashCode 一直不变,Text widget 每次都是新对象
@前端进阶之旅: 代码已经复制到剪贴板

# 容易说错的地方

很多人说「Flutter 是 Dart 写的」,其实只有 Framework 是,Engine 是 C++,所以排查渲染卡顿时要看 DevTools 里 UI 线程和 Raster 线程两条线:UI 线程慢是你的 build / layout 重,Raster 线程慢是画的东西重(比如大量 saveLayer、模糊、裁剪)。另一个是渲染后端的版本差异:iOS 从 Flutter 3.10 起默认 Impeller,Android 从 3.27 起在支持 Vulkan 的设备上默认 Impeller,老设备会回落,讲渲染层时别只说 Skia。

面试官追问

追问 1setState 之后整个页面的 build 都跑了,是不是整页都重绘了?
参考回答

不是。build 只生成新配置,Element 比对后复用节点,RenderObject 只有属性变了才 markNeedsPaint。想确认就开 debugRepaintRainbowEnabled = true,颜色变了的区域才是真重绘的。

追问 2同样一个列表,UI 线程很闲,Raster 线程一直超 16ms,问题在哪一层?
参考回答

在 Engine 的光栅化,不在你的 build。常见是 Opacity、ClipRRect、BackdropFilter 触发了 saveLayer,或者图片解码尺寸太大。先用 DevTools 的 Raster 面板找贵的 layer,图片记得配 cacheWidth。

追问 3什么时候需要自己写 RenderObject,而不是组合现有 Widget?
参考回答

组合实现不了、或者组合层级深到影响性能的布局才写,比如按特殊规则排的标签流、需要在一次 layout 里拿到子节点尺寸再决定位置的场景。99% 的业务用 CustomPaint、CustomMultiChildLayout 就够了,直接写 RenderBox 维护成本不小。

追问 4Element 复用的判断依据是什么?列表项状态串了跟它有关系吗?
参考回答

看 runtimeType 和 key 是否都相同。列表删掉第一项后,没有 key 的话框架按位置匹配,第二项的 Widget 会复用第一项的 Element 和 State,于是状态串位。给列表项加 ValueKey(item.id) 就好。

# 2. Flutter 为什么能一套代码在多个平台上画出一样的 UI?

30 秒速记

  • 根本原因是自绘:不用系统控件,Engine 自己用 Skia / Impeller 往一块画布上画像素
  • 平台只需要提供三样:一块 Surface、vsync 信号、输入事件,这部分由 Embedder 适配
  • 代价:长得不像原生(要自己做 Cupertino / 适配)、无障碍和输入法要靠 Semantics 和平台桥接补
  • 嵌原生控件(地图、WebView)要走 PlatformView,有额外合成开销
  • Web 端渲染器:CanvasKit / skwasm,旧的 HTML renderer 已在 3.29 移除

Flutter 能多端一致,是因为它压根不用系统的 UI 控件,所有东西都是 Engine 自己画的。 每个平台对 Flutter 来说只是「给我一块画布、告诉我什么时候该画下一帧、把用户的点击传给我」,这些由很薄的 Embedder 适配,剩下的布局、绘制、动画、手势全在跨平台的 Dart 和 C++ 里完成,所以同一份代码出来的像素基本一样。代价也很明确:按钮长得不会自动像 iOS 原生,系统控件的新特性要等 Flutter 自己实现,地图和 WebView 这种只能嵌原生控件,嵌进来有性能成本。

#

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础进阶

  • 精选模块

    • 精选篇

      • 1 HTML模块
      • 2 CSS模块
      • 3 JS模块
      • 4 ES6模块
      • 5 浏览器模块
      • 6 React模块
      • 7 Vue模块
      • 8 Node模块
      • 9 前端工程相关
      • 10 移动多端开发
      • 11 小程序模块
      • 12 Uniapp模块
      • 13 前端安全模块
      • 14 性能优化相关
      • 15 HTTP模块
      • 16 常用设计模式
      • 17 框架通识
      • 18 排序算法
      • 19 计算机通识
      • 20 React Native模块
      • 21 桌面端模块
      • 22 Flutter模块
        • 1. 架构与选型
          • 1. Flutter 的整体架构分哪几层?各层干什么?
          • 2. Flutter 为什么能一套代码在多个平台上画出一样的 UI?
          • 3. Flutter 和 React Native 的渲染路径有什么不一样?
          • 4. 新项目用 Flutter 还是原生,你怎么判断?
          • 5. Flutter、Electron、Tauri 做桌面应用,各自适合什么场景?
          • 6. Flutter 的 Framework、Engine、Embedder 分别负责什么?一帧是怎么配合出来的?
          • 7. Flutter 为什么选 Dart,而不是 JavaScript 或 Kotlin?
          • 8. Flutter 的 Debug、Profile、Release 三种模式有什么区别?
          • 9. Flutter 是怎么调用原生能力的?平台适配层怎么工作?
          • 10. 一个 Flutter 应用从点开图标到出现首帧,中间经历了什么?
        • 2. Dart 类型系统与空安全
        • 3. Dart 面向对象与泛型
        • 4. Future、Stream 与事件循环
        • 5. Isolate 与并发
        • 6. Widget、Element 与 RenderObject
        • 7. 状态与生命周期
        • 8. BuildContext、Key 与依赖
        • 9. 布局与约束
        • 10. 渲染、绘制与合成
        • 11. 手势、输入与表单
        • 12. 动画
        • 13. 滚动、列表与 Sliver
        • 14. 导航、深链与状态恢复
        • 15. 状态管理与应用架构
        • 16. 网络、存储与离线
        • 17. 平台通道、插件与 Add-to-App
        • 18. 性能、内存与 DevTools
        • 19. 测试、调试与可访问性
        • 20. 构建、发布、安全与跨平台
        • 21. Flutter、React Native 与 uni-app 高频对比
        • 22. 工程工具链与依赖治理
        • 23. GetX 与状态管理实战
        • 24. 业务能力与平台集成
        • 25. Dart 集合、函数式与异常实战
        • 官方资料
      • 23 Android模块
      • 24 iOS模块
    • 进阶篇

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

22-flutter

# Flutter 面试题精选

覆盖 Dart、Flutter Framework、渲染、状态、并发、平台集成、性能、测试、发布、工程实战,以及 Flutter / React Native / uni-app 高频对比。每题均含速记、代码、详细解析和追问;核心流程可逐帧交互。

# 1. 架构与选型

# 1. Flutter 的整体架构分哪几层?各层干什么?

30 秒速记

  • 三层:Framework(Dart)→ Engine(C++)→ Embedder(各平台原生代码),越往下越贴近系统
  • Framework 内部再分:foundation → animation / painting / gestures → rendering → widgets → Material / Cupertino
  • 三棵树:Widget 是配置(轻、不可变、随时重建),Element 是实例和身份(持有 State),RenderObject 管布局和绘制
  • build 被调用 ≠ 重新布局 ≠ 重新绘制,中间有 Element 做比对,只有属性真变了才往下标脏
  • 业务代码几乎只碰 widgets 及以上,自定义布局/绘制才下到 rendering 层

Flutter 自上而下是 Framework、Engine、Embedder 三层,业务写的 Dart 代码全在 Framework 里。 Framework 负责把你的 Widget 变成一棵渲染树,算出布局、生成绘制指令;Engine 是 C++ 写的,带着 Dart 运行时、文字排版和渲染后端(Skia 或 Impeller),把绘制指令真正变成像素;Embedder 是每个平台的壳,负责建窗口、接 vsync、转发触摸和生命周期。面试我会顺带讲三棵树:Widget 只是一份配置,重建很便宜,Element 才是真正挂在树上的身份,RenderObject 干重活,所以「频繁 build」本身不一定是性能问题。

# 先用大白话把三层讲清楚

可以把 Flutter 应用想成一个自带画笔的游戏:Framework 是游戏逻辑,决定画什么;Engine 是画笔和画布,负责真的画出来;Embedder 是游戏机的外壳,管屏幕、按键和开关机。原生 Android 的按钮是系统画的,Flutter 的按钮是 Engine 自己一笔一笔画的,系统只提供一块空白画布。

层 语言 主要内容 业务会不会碰
Framework Dart widgets、rendering、手势、动画、Material / Cupertino 组件库 天天碰
Engine C++ Dart VM、Skia / Impeller、文字排版、platform channel 的底层通道 基本不碰
Embedder Java/Kotlin、ObjC/Swift、C++ 窗口/Surface、线程、vsync、输入事件、插件注册 写插件、混合开发时碰

# 三棵树是 Framework 层的核心

Widget 是不可变的配置对象,每次 setState 都会生成一批新的;Element 是框架根据 Widget 创建的长期实例,它拿新旧 Widget 比较 runtimeType 和 key,一样就复用自己,只把新配置递给下面的 RenderObject;RenderObject 收到新属性后,只有值真的变了才调用 markNeedsLayout 或 markNeedsPaint。所以一帧里 build 可能跑了很多次,但真正重新布局、重新绘制的节点可能很少。

下面这段能直接跑,看清 BuildContext 其实就是 Element,以及同一个 Element 在重建后不变:

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: Counter()));

class Counter extends StatefulWidget {
  const Counter({super.key});
  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int n = 0;

  @override
  Widget build(BuildContext context) {
    // context 就是当前 Element,每次重建打印的 hashCode 不变
    debugPrint('element=${identityHashCode(context)} widget=${identityHashCode(widget)}');
    return Scaffold(
      body: Center(
        child: Builder(builder: (ctx) {
          // 拿到 Text 下面那个 RenderObject(RenderParagraph)
          WidgetsBinding.instance.addPostFrameCallback((_) {
            debugPrint('render=${ctx.findRenderObject().runtimeType}');
          });
          return Text('$n');
        }),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => n++),
        child: const Icon(Icons.add),
      ),
    );
  }
}
// 点几次按钮:element 的 hashCode 一直不变,Text widget 每次都是新对象
@前端进阶之旅: 代码已经复制到剪贴板

# 容易说错的地方

很多人说「Flutter 是 Dart 写的」,其实只有 Framework 是,Engine 是 C++,所以排查渲染卡顿时要看 DevTools 里 UI 线程和 Raster 线程两条线:UI 线程慢是你的 build / layout 重,Raster 线程慢是画的东西重(比如大量 saveLayer、模糊、裁剪)。另一个是渲染后端的版本差异:iOS 从 Flutter 3.10 起默认 Impeller,Android 从 3.27 起在支持 Vulkan 的设备上默认 Impeller,老设备会回落,讲渲染层时别只说 Skia。

面试官追问

追问 1setState 之后整个页面的 build 都跑了,是不是整页都重绘了?
参考回答

不是。build 只生成新配置,Element 比对后复用节点,RenderObject 只有属性变了才 markNeedsPaint。想确认就开 debugRepaintRainbowEnabled = true,颜色变了的区域才是真重绘的。

追问 2同样一个列表,UI 线程很闲,Raster 线程一直超 16ms,问题在哪一层?
参考回答

在 Engine 的光栅化,不在你的 build。常见是 Opacity、ClipRRect、BackdropFilter 触发了 saveLayer,或者图片解码尺寸太大。先用 DevTools 的 Raster 面板找贵的 layer,图片记得配 cacheWidth。

追问 3什么时候需要自己写 RenderObject,而不是组合现有 Widget?
参考回答

组合实现不了、或者组合层级深到影响性能的布局才写,比如按特殊规则排的标签流、需要在一次 layout 里拿到子节点尺寸再决定位置的场景。99% 的业务用 CustomPaint、CustomMultiChildLayout 就够了,直接写 RenderBox 维护成本不小。

追问 4Element 复用的判断依据是什么?列表项状态串了跟它有关系吗?
参考回答

看 runtimeType 和 key 是否都相同。列表删掉第一项后,没有 key 的话框架按位置匹配,第二项的 Widget 会复用第一项的 Element 和 State,于是状态串位。给列表项加 ValueKey(item.id) 就好。

# 2. Flutter 为什么能一套代码在多个平台上画出一样的 UI?

30 秒速记

  • 根本原因是自绘:不用系统控件,Engine 自己用 Skia / Impeller 往一块画布上画像素
  • 平台只需要提供三样:一块 Surface、vsync 信号、输入事件,这部分由 Embedder 适配
  • 代价:长得不像原生(要自己做 Cupertino / 适配)、无障碍和输入法要靠 Semantics 和平台桥接补
  • 嵌原生控件(地图、WebView)要走 PlatformView,有额外合成开销
  • Web 端渲染器:CanvasKit / skwasm,旧的 HTML renderer 已在 3.29 移除

Flutter 能多端一致,是因为它压根不用系统的 UI 控件,所有东西都是 Engine 自己画的。 每个平台对 Flutter 来说只是「给我一块画布、告诉我什么时候该画下一帧、把用户的点击传给我」,这些由很薄的 Embedder 适配,剩下的布局、绘制、动画、手势全在跨平台的 Dart 和 C++ 里完成,所以同一份代码出来的像素基本一样。代价也很明确:按钮长得不会自动像 iOS 原生,系统控件的新特性要等 Flutter 自己实现,地图和 WebView 这种只能嵌原生控件,嵌进来有性能成本。

# 自绘和「映射原生控件」是两条路

跨平台框架大致两类:一类把你的组件映射成系统控件(React Native 的 View 最后是 android.view.View / UIView),一类自己画(Flutter)。映射的好处是天然原生观感,坏处是两个平台控件行为不一致,适配要写两遍;自绘正好相反,一致性拉满,但「像原生」要自己追。

Flutter 画一个按钮的过程,跟系统控件完全无关:ElevatedButton 最终变成若干 RenderObject,它们在 paint 里往 Canvas 上画圆角矩形、阴影和文字,生成一棵 Layer 树,交给 Engine 光栅化成纹理,再由 Embedder 提交到平台的 Surface(Android 上是 SurfaceView / TextureView,iOS 上是 CAMetalLayer)。平台只看到一张不断刷新的图。

# 一致是默认的,差异要你主动加

← 21 桌面端模块23 Android模块 →

fe