一、Widget的生命周期
1、StatelessWidget
StatelessWidget只有build的过程,是用来创建widget的。
2、StatefullWidget
* createState():在StatefulWidget第一次插入树时调用,用于创建State对象。
* initState():初始化State对象,设置初始状态。
* didChangeDependencies():State依赖的InheritedWidget发生变化时调用。
* build():每次setState()被调用时,重新构建UI。
* didUpdateWidget():父widget重新构建时调用,用于比较新旧Widget的差异。
* deactivate():State对象从树中移除时调用,用于清理资源。
* dispose():State对象永久从树中移除时调用,进行资源释放。
二、Flutter的渲染流程三棵树
Widget树:描述UI的结构,是不可变的声明式配置。
Element树:Widget的实例,管理Widget与渲染对象的关系,并保存UI状态。
Render树:负责实际渲染内容,计算布局和绘制。
三、Flutter的底层结构
Framework(Dart):提供动画、手势、绘制能力,widget提供基础组件。Material/Cupertion提供iOS和Android风格的组件库。
Engine:使用C/C++实现的,执行渲染、线程管理、平台事件等操作。
Embedder : 平台到中间层的渲染设置、原生插件、打包等处理。
四、怎么让组件不随着状态变化而重新构建,避免不必要的性能开销。
* 使用 const 构造函数避免不必要的重建。
* 使用 ValueListenableBuilder 或 StreamBuilder 来精细控制哪些部分需要更新。
* 使用 RepaintBoundary 来避免不必要的绘制。使用它包裹的widget在父视图重绘的时候不会重绘。 Repaint:重绘 Boundary:范围,界限。
* 使用状态管理工具(如 Provider, Riverpod)来控制更新粒度。Provider 时,如果某个组件只依赖于某一部分状态,你可以使用 Consumer 或 Selector 来精确控制哪些部分需要更新。
* 通过 setState 和局部更新来优化组件性能。
* 使用 GlobalKey 或 Key 来控制组件的重新创建。
五、Flutter的methodchannel和eventchannel的区别是什么?
1、Flutter methodchannel典型调用原生方法并获取结果setMethodCallHandler通过block返回数据。
2、Flutter eventchannel它允许从原生端向Flutter端持续发送数据流,传感器数据、设备状态更新、持续的原生事件(比如GPS位置更新、传感器数据流等),通过eventSink不断的将数据发送到Flutter端。
特性 | MethodChannel | EventChannel |
---|---|---|
通信类型 | 请求-响应(一次性请求和响应) | 数据流(持续推送事件) |
用途 | 用于请求单次操作并获取返回结果 | 用于实时、连续的事件流或数据推送 |
Flutter端 | 使用invokeMethod()发送请求,获取结果 | 使用receiveBroadcastStream()接收事件流 |
原生端 | 使用result.success(), result.error()返回结果 | 使用eventSink.success()推送数据流 |
使用场景 | 调用一次性操作,如获取设备信息、执行任务等 | 处理实时数据流,如GPS位置、传感器数据等 |
六、iOS和Flutter互相调用并传参MethodChannel
1、首先创建FlutterEngine
lazy var flutterEngine = FlutterEngine(name: "io.flutter", project: nil)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 初始化Flutter引擎
flutterEngine.run()
// 注册所有Flutter插件
GeneratedPluginRegistrant.register(with: flutterEngine)
return true
}
2、MethodChannel发送数据,创建MethodChannel对象
2.1 通过flutterEngine,创建初始化FlutterViewController
2.2 创建FlutterMethodChannel
2.3 通过methodChannel.invokeMethod向Flutter页面传参
2.4 通过methodChannel.setMethodCallHandler{ (call, result) in } 的block函数监听Flutter页面调用原生的事件名。 call.method == "closeFlutterPage"
2.5 通过result将数据返回到Flutter页面中。
@objc func useFlutter() {
// 初始化FlutterViewController并展示
let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
//新的方法,在初始化的时候直接使用
let initialRoute = "/flutterPage" //需要跳转的路由界面
// let flutterViewController = FlutterViewController(engine: flutterEngine!, nibName: nil, bundle: nil, initialRoute: initialRoute) 新版
// flutterViewController.setInitialRoute("/flutterPage") // 方法已经废弃,但是还可以使用
// 使用 MethodChannel 传递数据
let methodChannel = FlutterMethodChannel(name: "com.example.myapp/channel", binaryMessenger: flutterViewController as! FlutterBinaryMessenger)
// 监听方法调用
methodChannel.setMethodCallHandler { (call, result) in
if call.method == "closeFlutterPage" {
if let arguments = call.arguments as? [String: Any], let data = arguments["data"] as? String {
print("Received data: \(data)")
self.clickBtn.setTitle("flutter \(data)", for: .normal)
result("Data received successfully") // 返回给 Flutter
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing 'data'", details: nil))
}
self.closeFlutterPage(controller: flutterViewController)
result("Flutter page closed successfully")
}
}
// 向 Flutter 页面传递参数
methodChannel.invokeMethod("receiveData", arguments: ["message": "Hello from iOS!"])
present(flutterViewController, animated: true, completion: nil)
}
3、在Flutter的对应页面中的init方法中监听调用,
static const platform = MethodChannel('com.example.myapp/channel');
void initState() {
super.initState();
// 监听来自 iOS 的数据
platform.setMethodCallHandler((call) async {
if (call.method == 'receiveData') {
setState(() {
param1 = call.arguments['param1'];
param2 = call.arguments['param2'];
}); } }); }
tips:两种方法传参
1、使用 setInitialRoute 传递参数:你可以通过 URL 路由将参数传递给 Flutter 页面,这对于简单的参数传递很方便。
2、使用 MethodChannel 传递参数:如果你需要更复杂的数据传递或双向通信,可以使用 MethodChannel 在 iOS 和 Flutter 之间传递数据。
七、Flutter的FlutterEventChannel
1、在Flutter页面先创建 FlutterEventChannel,并指定通道名称。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// 创建EventChannel并指定通道名称
static const eventChannel = EventChannel('com.example.eventchannel');
String _nativeData = "No data received";
@override
void initState() {
super.initState();
_startListening();
}
// 监听来自原生平台的事件
void _startListening() {
eventChannel.receiveBroadcastStream().listen(
(data) {
setState(() {
_nativeData = data.toString();
});
},
onError: (error) {
setState(() {
_nativeData = "Error: $error";
});
},
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Flutter EventChannel Example"),
),
body: Center(
child: Text('Data from native: $_nativeData'),
),
),
);
}
}
2、在swift工程中
2.1 创建FlutterEventChannel。
2.2 通过协议方法eventSink不断的给Flutter发送数据。
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
private var eventSink: FlutterEventSink?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller = window?.rootViewController as! FlutterViewController
let eventChannel = FlutterEventChannel(name: "com.example.eventchannel",
binaryMessenger: controller.binaryMessenger)
eventChannel.setStreamHandler(self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
// 实现 FlutterStreamHandler 协议
extension AppDelegate: FlutterStreamHandler {
// 当 Flutter 开始监听数据流时
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
self.eventSink = events
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { timer in
self.eventSink?("Native Event Data: \(Date())")
}
return nil
}
// 当 Flutter 停止监听数据流时
func onCancel(withArguments arguments: Any?) -> FlutterError? {
eventSink = nil
return nil
}
}
七、Flutter中是怎么实现异步操作的?
三种方式:Future async、 isolate 和 compute
使用 Future:当任务是异步的、I/O 密集型的(如网络请求、数据库操作、文件读取)时,使用 Future 是最合适的。
使用 Isolate:当你需要进行复杂的并行计算或 CPU 密集型任务时(如图像处理、视频转码等),使用 Isolate 可以在独立的线程中执行任务,避免主线程阻塞。
使用 compute:当你希望简化并发计算任务的处理,且任务相对轻量时,compute 是一个简化版的 Isolate,可以帮助你轻松地将任务放到后台执行。
优缺点:
1、虽然compute方法可以帮助我们轻易实现异步操作的要求,但是compute也有缺陷,由于compute是由flutter控制管理Isolate,当我们面临复杂场景,频繁使用compute会造成大量的Isolate创建和销毁。损耗性能比较大。
2、直接使用Isolate可以实现对之前创建的Isolate的复用。 Isolate之间的通信通过port端口实现。通过 ReceivePort 和 SendPort 进行通信传递异常
特性 | Future | Isolate | compute |
---|---|---|---|
并发模型 | 单线程异步执行(事件循环) | 多线程并行执行(独立内存空间) | 封装了 Isolate,使用多线程并行执行计算 |
内存共享 | 主线程与 Future 共享内存 | 每个 Isolate 有独立的内存空间,不能共享内存 | 每个 Isolate 有独立内存,通过消息传递通信 |
执行场景 | I/O 密集型任务,如网络请求、文件操作等 | CPU 密集型任务,如复杂计算、大数据处理等 | 轻量级的 CPU 密集型任务,通常用于后台计算 |
性能开销 | 较低,适合轻量级的异步任务 | 较高,适合复杂并行任务 | 较低,适合需要轻量级计算的并发任务 |
错误处理 | 使用 try/catch 或 Future.catchError 处理异常 | 通过 ReceivePort 和 SendPort 进行通信传递异常 | 异常通过 Future 的机制处理 |
复杂性 | 简单,适用于 I/O 操作和简单的异步任务 | 较复杂,需要手动管理线程创建和通信 | 简化了 Isolate 的创建和管理,易于使用 |
八、Flutter 性能优化点
- 使用 const 构造函数避免不必要的重建。
- 使用 const 修饰数据类,可以让Dart编译器将他们优化为单利对象,避免实例化。
const Color MainClor = Colors.red
- 使用 ValueListenableBuilder 或 StreamBuilder 来精细控制哪些部分需要更新。
- 使用 RepaintBoundary 来避免不必要的绘制。使用它包裹的widget在父视图重绘的时候不会重绘。 Repaint:重绘 Boundary:范围,界限。
- 使用状态管理工具(如 Provider, Riverpod)来控制更新粒度。Provider 时,如果某个组件只依赖于某一部分状态,你可以使用 Consumer 或 Selector 来精确控制哪些部分需要更新。
- 通过 setState 和局部更新来优化组件性能。
- 使用 GlobalKey 或 Key 来控制组件的重新创建。
- 使用 AutomaticKeepAlive 保存状态:
对于 ListView 或 PageView 等带有滚动特性的组件,可以使用 AutomaticKeepAlive 来保留离屏的 Widget 状态,从而避免重建。
class MyListItem extends StatefulWidget {
@override
_MyListItemState createState() => _MyListItemState();
}
class _MyListItemState extends State<MyListItem> with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context); // 必须调用
return Text('I am kept alive');
}
}
- ValueNotifier 和 ChangeNotifier 是轻量级的状态管理工具,可以减少 setState 的使用并精确更新 UI 中的部分。
class MyWidget extends StatelessWidget {
final ValueNotifier<int> counter = ValueNotifier<int>(0);
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<int>(
valueListenable: counter,
builder: (context, value, child) {
return Text('Counter: $value');
},
);
}
}