Flutter总结

一、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');
      },
    );
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Dart部分 String扩展一个方法 使用关键字extension ... on为String定义一个扩展类 在...
    渚清与沙白阅读 2,088评论 1 6
  • 0、Dart是值传递还是引用传递? Dart是值传递。每次调用函数,传递过去的都是对象的内存地址,不是对象的复制。...
    woniu阅读 3,354评论 1 23
  • Dart 相关 1、Dart 当中的 「..」表示什么意思? 级连操作符 “..” 和 “.” 不同:调用..后返...
    af06e7def7a7阅读 2,332评论 0 2
  • 一、flutter启动流程1.实例化WidgetsFlutterBinding类,2.创建组件树attachRoo...
    齐玉婷阅读 2,898评论 0 9
  • Flutter 架构 Flutter框架分三层Framework,Engine, Embedder Framewo...
    SimpleFunc阅读 4,425评论 3 7