Flutter 是 Google 开源的应用开发框架,仅通过一套代码库,就能构建精美的、原生平台编译的多平台应用。
Flutter 只要一套代码兼顾Android、iOS、Web、Windows、macOS和Linux六个平台。
Flutter 由Dart语言强力驱动,助力高效构建全平台应用。
前言
组件化一直是移动端比较流行的开发方式,有着编译运行快,业务逻辑分明,任务划分清晰等优点。大多数情况下,我们创建一个 Flutter 项目,默认是一个单包项目,该项目由一个pubspec.yaml、lib文件夹组成。所有的逻辑和功能都在这一个包中实现,但是也有一些复杂的 Flutter 项目会按功能,模块,组件等拆分为多个包。
相对应的 Flutter 项目的组件化也是一样的道理,我们也能把各个功能独立的组件比如用户模块、支付模块等,这些模块在项目以单独项目的方式存在,通过本地 path 依赖或者 melos 的方案去组合起来,由于 Flutter 项目的特殊性,我们还能在单独模块项目中可以创建各自的 sample 模块去独立运行这个模块从而实现组件化。
方案
-
组件化工具
Melos是一个专为Dart和Flutter项目设计的工具,旨在简化包含多个包的monorepo(单一代码库)的管理工作。Melos的主要功能包括多包管理、依赖同步、脚本执行、版本管理和开发环境一致性等。 -
架构设计模式
-
常用库选择
状态管理:riverpod,flutter_hooks,hooks_riverpod
路由管理:auto_route
网络请求:dio
进度条:percent_indicator
设备信息:device_info_plus
图片库:cached_network_image
首次初始化:after_layout
跨组件通信:event_bus
图片预览、缩放:photo_view
动画库:animations
轮播图:carousel_slider
图表:fl_chart
屏幕适配工具:flutter_screenutil
无线滚动:infinite_scroll_pagination
权限申请:permission-handler
包信息:package_info_plus
文件选择:file_picker
返回键拦截器:back_button_interceptor
可见性监听:visibility_detector
无障碍工具:accessibility_tools
解压缩文件:archive
路径处理:path
deeplink:url_launcher
分享:share_plus
裁剪:croppy
文字播报:flutter_tts
图片选择:image_picker
网路监听:connectivity_plus
状态观察者库:rxdart
HTML解析:html
评级:flutter_rating_bar
打开文件:open_file
网络信息:network_info_plus
上拉下拉:pull_to_refresh,easy_refresh
webview:webview_all
查找文件:path_provider
文件保存:file_saver
保存到相册:image_gallery_saver
唤醒:wakelock_plus
sharedpreferences:shared_preferences
数据库:sqflite_common
骨架屏:skeletonizer
基础工具类:basic_utils
高级富文本:extended_text_library
剪切板:clipboard
键盘监听:flutter_keyboard_visibility
自适应大小文本:auto_size_text
自适应大小文本框:auto_size_text_field
二维码:qr,barcode,zxing_lib
截图:screenshot,
扩展组件:expandable
toast:bot_toast
文件缓存:flutter_cache_manager
打开文件:open_file
日志:logger
通知:awesome_notifications
步骤线:stepper_list_view
日历:table_calendar,calendar_date_picker2
json解析:json_serializable,dart_mappable
地图:flutter_map,map,maps_launcher,maps_toolkit,flutter_map_location_marker
… -
项目结构

避坑/面试
-
dynamic,var,object三者的区别
dynamic 允许变量在运行时动态改变类型,编译器不进行类型检查。
var 是类型推断声明,变量类型在声明时确定,之后不能改变。
Object 是所有 Dart 对象的基类,允许任何类型的值,但需要进行类型转换才能使用具体类型的值。 -
flutter里的key有什么用
GlobalKey 是一个全局唯一的键,用于标识整个应用中的特定 Widget。它可以跨越 Widget 树的多个层次来访问 Widget,并提供更强的控制和状态管理。
LocalKey 是 Key 的基类,分为两种:ValueKey 和 ObjectKey,用于在局部 Widget 树中唯一标识某个 Widget。
Key的作用:保持状态,优化性能,唯一标识。 -
flutter中Widget的分类
组合类:StatelessWidget和StatefulWidget
生命周期对比生命周期方法 StatelessWidget StatefulWidget build是 是 createState否 是 initState否 是 didChangeDependencies否 是 didUpdateWidget否 是 deactivate否 是 dispose否 是 代理类:inheritedwidget、ParentDataWidget
inheritedwidget一般用于在Widget树中传递共享数据,状态共享,如Theme 、Localizations 、 MediaQuery 等,都是通过它实现共享状态,这样我们可以通过 context 去获取共享的状态,比如 ThemeData theme = Theme.of(context);绘制类:RenderObjectWidget
RenderObject 的布局相关方法调用顺序是 : layout -> performResize -> performLayout -> markNeedsPaint -
mixin extends implement 之间的关系
继承(关键字 extends)、混入 mixins (关键字 with)、接口实现(关键字 implements)。
在Flutter中,Mixins是一种在多个类层次结构中复用类代码的方法。mixins的对象是类,mixins绝不是继承,也不是接口,而是一种全新的特性,可以mixins多个类,mixins的使用需要满足一定条件。使用mixins的条件:
mixins类只能继承自object
mixins类不能有构造函数
一个类可以mixins多个mixins类
可以mixins多个类,不破坏Flutter的单继承这三者可以同时存在,前后顺序是extends -> mixins -> implements。 -
async、await、Future 和 Stream
async 和 await:用于简化异步操作的处理,使代码看起来像同步代码。
Future:表示一次性异步操作的结果,完成后返回单一结果。
Stream:表示多个异步数据序列,支持多次返回数据更新。 -
flutter中的多线程
Isolate 是 Dart 中的一个并发模型,类似于线程,但每个 Isolate 有自己独立的内存空间。Isolate 之间不能直接共享内存,需要通过消息传递来通信。
Future 和 async/await 用于异步操作,常用于网络请求、文件读取等需要等待结果的任务。使用 await 等待 Future 的结果。
await for 用于异步地遍历 Stream,适合处理一系列异步数据的场景。 -
Stream 有哪两种订阅模式
Stream 类提供了两种订阅模式:单订阅模式(single-subscription)和广播模式(broadcast)。单订阅就是只能有一个订阅者,而广播是可以有多个订阅者。
Stream 默认处于单订阅模式,所以同一个 stream 上的 listen 和其它大多数方法只能调用一次,调用第二次就会报错。但 Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。
-
BuildContext 具体是什么东西
在 Flutter 中,BuildContext 表示了当前 Widget 在树中的位置和上下文信息。它提供了与当前 Widget 相关的信息,如父级 Widget、Theme、MediaQuery、Navigator 等,以便在构建 UI 时使用。BuildContext 实际上是一个指向当前 Widget 在 Widget 树中位置的引用。
BuildContext 的作用:
定位位置:BuildContext 可以告诉你当前 Widget 在 Widget 树中的位置,如父级 Widget 是什么、祖先 Widget 是什么等。
获取相关信息:通过 BuildContext 可以获取当前 Widget 上下文相关的信息,如主题、设备信息、国际化信息等。
构建 UI:在构建 Widget 时,通常需要使用 BuildContext 来访问上下文信息,例如使用 Theme.of(context) 获取当前主题。 -
Dart异步编程中的Future关键字作用
Dart中,执行一个异步任务使用Future来处理。在 Dart 的每一个 Isolate 当中,执行的优先级为 :Main > MicroTask > EventQueue。 -
描述Flutter的核心渲染模块三棵树
WidgetTree:存放渲染内容、它只是一个配置数据结构,创建是非常轻量的,在页面刷新的过程中随时会重建Element 是分离 WidgetTree 和真正的渲染对象的中间层, WidgetTree 用来描述对应的Element 属性,同时持有Widget和RenderObject,存放上下文信息,通过它来遍历视图树,支撑UI结构。
RenderObject (渲染树)用于应用界面的布局和绘制,负责真正的渲染,保存了元素的大小,布局等信息,实例化一个 RenderObject 是非常耗能的
当应用启动时 Flutter 会遍历并创建所有的 Widget 形成 Widget Tree,通过调用 Widget 上的 createElement() 方法创建每个 Element 对象,形成 Element Tree。最后调用 Element 的 createRenderObject() 方法创建每个渲染对象,形成一个 Render Tree。
-
Flutter三棵树设计的核心原因与优势
分层解耦:职责分离与性能优化。
高效更新:Element树的Diff机制与RenderObject复用。
状态持久化:Element维护动态数据。
跨平台一致性:RenderObject统一渲染管线。 -
Flutter线程管理模型
Flutter Engine层会创建一个Isolate,并且Dart代码默认就运行在这个主Isolate上。必要时可以使用spawnUri和spawn两种方式来创建新的Isolate,在Flutter中,新创建的Isolate由Flutter进行统一的管理。
事实上,Flutter Engine自己不创建和管理线程,Flutter Engine线程的创建和管理是Embeder负责的,Embeder指的是将引擎移植到平台的中间层代码。Flutter 中存在的四大线程:分别为 UI Runner、GPU Runner、IO Runner, Platform Runner (原生主线程) ,
在 Flutter 中可以通过 isolate 或者 compute 执行真正的跨线程异步操作。
-
Future还是isolate使用场景分析
如果代码段可以独立运行而不会影响应用程序的流畅性,建议使用 Future (需要花费几毫秒时间)。
如果繁重的处理可能要花一些时间才能完成,而且会影响应用程序的流畅性,建议使用 isolate (需要几百毫秒)。 -
Flutter是如何与原生Android、iOS进行通信的
Flutter 通过 PlatformChannel 与原生进行交互,其中 PlatformChannel 分为三种:
BasicMessageChannel :用于传递字符串和半结构化的信息。
MethodChannel :用于传递方法调用(method invocation)。
EventChannel : 用于数据流(event streams)的通信。 -
性能优化
-
避免Widget无效的重建
使用const构造函数,对于不会改变的Widget,使用const构造函数创建常量Widget,可以避免不必要的重建。
合理使用Key,在ListView或GridView等可滚动列表中,为列表项指定Key可以帮助Flutter识别哪些项发生了变化,从而只重建发生变化的项。
避免在build方法中进行复杂操作,build方法应该只负责构建UI,避免在其中进行复杂的计算或数据处理。 -
长列表滚动性能问题
使用 ListView.builder:对于长列表,使用 ListView.builder,它只在滚动时创建可见的项目。
禁用addRepaintBoundaries(默认开启):对简单列表项关闭重绘边界,减少渲染管线开销。
禁用addAutomaticKeepAlives(默认开启):减少内存占用,必要时手动实现AutomaticKeepAliveClientMixin保持状态。
使用KeepAlive组件配合SliverPersistentHeader,优化头部/尾部固定内容的性能。
使用 CacheExtent:设置 cacheExtent 提前加载部分项目,避免滚动时卡顿。
图片加载优化:使用 FadeInImage 或 cached_network_image 库优化图片加载。
设置ScrollAwareImageProvider:判断快速滑动, 下载和解码会停止。
使用懒加载与分页,避免一次性请求过多资源。
通过 evictCallback 和 dispose() 确保图片/纹理等敏感资源释放。1
2
3
4CachedNetworkImage(
imageUrl: url,
evictCallback: (url) => true, // 列表项销毁时触发缓存回收
) -
使用异步操作
使用 compute 函数:将耗时操作移到后台 Isolate 执行,避免阻塞主线程。
异步编程:使用 Future、Stream 和 async/await 进行异步编程,避免阻塞UI。 -
布局和绘制性能问题
避免不必要的层级:尽量减少 Widget 层级,避免过度嵌套。
使用 RepaintBoundary:将需要频繁重绘的部分包裹在 RepaintBoundary 中,减少重绘范围。
布局缓存:使用 AutomaticKeepAliveClientMixin 保持列表项的状态,避免重建。
-
Flutter错误收集
布局溢出
1、使用DebugPaintSizeEnabled将DebugPaintSizeEnabled widget包裹在你的应用根widget上,来在屏幕上绘制出每个widget的边界和大小。这有助于你直观地看到哪些widget可能超出了其父widget的边界。
2、检测工具 Flutter Inspector (debug模式下),使用DevTools
3、在你的widget树中,你可以编写自定义的溢出检测逻辑。这通常涉及到在widget的build方法或自定义RenderObject的performLayout方法中检查子widget的布局尺寸和父widget的约束。
异常收集
1、Dart层异常捕获;可以通过try-catch机制直接捕获
2、Flutter框架错误捕获
- 异步异常 Future.catchError适用于捕获异步操作中的异常
- 全局异常捕获 FlutterError.onError全局异常捕获
- 全局异步回调异常 PlatformDispatcher.instance.onError
- 使用Zone.runZoned集中捕获:(过时)
3、原生崩溃捕获:集成原生崩溃收集工具、接入firebase、bugly等
总结
总之,Flutter最大的优势在于其跨平台能力,社区成熟。使用Dart语言,可以同时为iOS、Android、PC端开发应用,减少了代码重复,提高了开发效率。但在跨平台上Jetpack Compose版本仅支持5.0以上,社区不如Flutter那样成熟,同时fultter最低版本支持4.1,可以根据业务选择使用。