聊聊Flutter与原生平台通信方式(一)

前言

随着移动应用开发需求的不断增长,开发者们越来越多地寻求跨平台的开发框架,以节省时间和成本。Flutter作为一款由 Google 推出的优秀跨平台开发框架,凭借其卓越的性能,受到广泛欢迎。然而,尽管 Flutter 提供了丰富的功能和控件库,在实际开发中,我们不可避免地需要与原生平台进行交互。

无论是访问设备硬件功能(如相机、传感器等),还是使用一些平台特有的功能(如调用系统API或第三方原生库),Flutter 与原生通信都是不可或缺的部分。通过灵活的通信机制,Flutter 可以轻松与 Android 和 iOS 原生代码进行数据交换和方法调用,从而打破平台的限制,实现跨平台应用中对原生功能的控制使用。

本文将介绍 Flutter 与原生平台通信的几种常见方式,并通过具体示例展示如何在实际项目中实现这些通信机制。

方案一 使用Method Channel

1.1 MethodChannel 介绍

MethodChannel 是 Flutter 和原生平台之间进行通信最常用的方式。它允许 Flutter 向原生平台(如 Android 和 iOS)发送消息,并接收来自原生平台的返回结果。通过 MethodChannel,可以调用原生平台的功能,如访问传感器、相机、存储等硬件资源,甚至使用特定于平台的 API。

特点:

  • 同步通信:MethodChannel 支持同步调用,即 Flutter 端发送请求后,等待原生平台执行完成后再返回结果。
  • 跨平台支持:同一套 MethodChannel 代码在 Flutter 端可以用于 Android 和 iOS 平台,只需要分别在各自的原生端实现对应的逻辑。
  • 常用场景:获取设备信息、调用本地硬件(如摄像头、传感器、GPS)、文件存储等操作。

1.2 创建步骤

  1. 导入 Flutter 服务包: 要使用平台通信功能,需要导入 package:flutter/services.dart 包。
  2. 创建 MethodChannel: 在 Flutter 端,通过 MethodChannel 构造函数创建通道实例。通道名称是一个字符串,用来标识该通道,这个名称要与原生平台端的 MethodChannel 名称保持一致。
  3. 调用原生方法: 使用 invokeMethod() 方法调用原生平台的方法,并通过 await 等待返回结果。

1.3 使用示例:调用原生平台的电池电量

Flutter 端实现

在 Flutter 端,我们通过 MethodChannel 调用原生方法并获取电池电量。 初始化 MethodChannel,用于与原生平台交互。 用户点击按钮后,Flutter 端通过 MethodChannel 调用原生平台方法获取电池电量。将获取到的电池电量数据显示在应用界面上。如果获取失败,显示对应的错误信息。

dart 复制代码
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> {
  // 创建一个 MethodChannel,用于与原生平台通信
  static const platform = MethodChannel('com.example.battery');

  // 变量用于存储电池电量的值
  String _batteryLevel = 'Unknown battery level.';

  // 定义一个方法,通过 MethodChannel 调用原生代码获取电池电量
  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      // 通过 MethodChannel 调用原生方法 'getBatteryLevel',获取结果
      final int result = await platform.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level at $result%.';
    } on PlatformException catch (e) {
      // 如果调用失败,捕获异常并显示错误信息
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    // 更新 UI
    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Battery Level Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              // 显示电池电量
              Text(_batteryLevel),
              // 按钮,点击时调用获取电池电量的方法
              ElevatedButton(
                onPressed: _getBatteryLevel,
                child: Text('Get Battery Level'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Android 端实现

在 Android 端使用 Kotlin 来获取电池电量信息,并通过 MethodChannel 返回给 Flutter。 Flutter 端调用 "getBatteryLevel" 方法获取电池电量。 Android 端检测到请求后,调用系统的 BatteryManager 获取电池电量。Android 端将结果通过 MethodChannel 返回给 Flutter,成功返回电量,失败则返回错误信息。

kotlin 复制代码
import android.content.Context
import android.os.BatteryManager
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    // 定义一个通道名称,用于与 Flutter 通信
    private val CHANNEL = "com.example.battery"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 创建 MethodChannel,并设置消息处理器
        MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "getBatteryLevel") {
                // 当 Flutter 调用 "getBatteryLevel" 时,获取电池电量
                val batteryLevel = getBatteryLevel()

                if (batteryLevel != -1) {
                    // 成功获取电量,返回结果给 Flutter
                    result.success(batteryLevel)
                } else {
                    // 获取电量失败,返回错误信息给 Flutter
                    result.error("UNAVAILABLE", "Battery level not available.", null)
                }
            } else {
                // 如果接收到未实现的方法调用,返回未实现状态
                result.notImplemented()
            }
        }
    }

    // 使用 BatteryManager 获取电池电量
    private fun getBatteryLevel(): Int {
        val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
        return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    }
}

iOS 端实现

在 iOS 端使用 Swift 获取电池电量,并通过 MethodChannel 返回给 Flutter。Flutter 端请求获取电池电量。iOS 端接收到请求后,启用电池监控并获取当前电池电量。iOS 端将电池电量以百分比形式返回给 Flutter,如果获取失败,返回错误信息。

swift 复制代码
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        // 创建 MethodChannel,与 Flutter 进行通信
        let batteryChannel = FlutterMethodChannel(name: "com.example.battery",
                                                  binaryMessenger: controller.binaryMessenger)

        // 设置消息处理器,处理从 Flutter 发送的消息
        batteryChannel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
            if call.method == "getBatteryLevel" {
                // 当 Flutter 调用 "getBatteryLevel" 时,获取电池电量
                self.receiveBatteryLevel(result: result)
            } else {
                // 如果接收到未实现的方法调用,返回未实现状态
                result(FlutterMethodNotImplemented)
            }
        }

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    // 获取设备的电池电量并返回给 Flutter
    private func receiveBatteryLevel(result: FlutterResult) {
        let device = UIDevice.current
        device.isBatteryMonitoringEnabled = true  // 开启电池监控

        // 判断电池状态是否为未知,如果是,则返回错误信息
        if device.batteryState == UIDevice.BatteryState.unknown {
            result(FlutterError(code: "UNAVAILABLE",
                                message: "Battery info unavailable",
                                details: nil))
        } else {
            // 返回电池电量(百分比)
            result(Int(device.batteryLevel * 100))
        }
    }
}

方案二 使用Event Channel

2.1 EventChannel 介绍

EventChannel 是 Flutter 和原生平台之间用于持续传递数据流的通信方式。与 MethodChannel 主要用于请求和响应的单次通信不同,EventChannel 适合于需要持续接收数据的场景,例如传感器数据、位置更新、系统事件等。

特点:

  • 双向通信:原生端可以持续向 Flutter 发送数据,Flutter 端则可以通过监听 EventChannel 来接收这些数据。
  • 适合流数据:如 GPS 数据、加速度传感器数据等,通常在原生端设置一个监听器,实时将数据发送给 Flutter 端。
  • 简单易用:通过 EventChannel 可以轻松实现实时数据流的监听和处理。

2.2 创建步骤

  1. 导入 Flutter 服务包: 首先,需要导入 package:flutter/services.dart 包以使用 EventChannel
  2. 创建 EventChannel 实例: 通过 EventChannel 构造函数创建通道实例,并指定一个唯一的通道名称。
  3. 设置监听器: 使用 receiveBroadcastStream() 方法开始监听来自原生端的数据流。

2.3 使用示例:监听Android加速度传感器数据

Flutter端通过 EventChannel 从原生端实时接收加速度传感器的数据,并在界面上动态显示这些数据。如果发生错误,会显示错误信息。

Flutter端代码:

dart 复制代码
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 = EventChannel('com.example.accelerometer');

  // 变量用于存储加速度传感器数据
  String _accelerometerData = 'Waiting for accelerometer data...';

  @override
  void initState() {
    super.initState();

    // 监听 EventChannel 传来的加速度数据
    eventChannel.receiveBroadcastStream().listen((dynamic event) {
      setState(() {
        // 更新界面显示的加速度数据
        _accelerometerData = 'Accelerometer data: $event';
      });
    }, onError: (dynamic error) {
      setState(() {
        _accelerometerData = 'Error receiving accelerometer data: $error';
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Accelerometer Data Example'),
        ),
        body: Center(
          child: Text(_accelerometerData),
        ),
      ),
    );
  }
}

Android 端实现(Kotlin)

在 Android 端,通过 EventChannel 将加速度传感器的数据实时传递给 Flutter 应用。Android 端通过注册传感器监听器,监测传感器数据的变化,并在数据变化时将 X、Y、Z 轴的加速度值发送给 Flutter 端。当 Flutter 端不再需要数据时,调用 onCancel 方法可以停止传感器监听,确保资源得以释放。

kotlin 复制代码
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.EventChannel

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.example.accelerometer"
    private lateinit var sensorManager: SensorManager
    private lateinit var accelerometer: Sensor
    private var sensorEventListener: SensorEventListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 获取传感器管理器
        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)

        // 创建 EventChannel,并设置 StreamHandler 处理传感器数据
        EventChannel(flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL).setStreamHandler(
            object : EventChannel.StreamHandler {
                override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
                    // 启动传感器监听
                    sensorEventListener = object : SensorEventListener {
                        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}

                        override fun onSensorChanged(event: SensorEvent?) {
                            // 当传感器数据发生变化时,将数据发送给 Flutter
                            if (event != null) {
                                val x = event.values[0]
                                val y = event.values[1]
                                val z = event.values[2]
                                // 将加速度数据通过 EventSink 发送给 Flutter 端
                                events?.success("x: $x, y: $y, z: $z")
                            }
                        }
                    }
                    sensorManager.registerListener(sensorEventListener, accelerometer, SensorManager.SENSOR_DELAY_NORMAL)
                }

                override fun onCancel(arguments: Any?) {
                    // 停止传感器监听
                    sensorManager.unregisterListener(sensorEventListener)
                    sensorEventListener = null
                }
            }
        )
    }
}

iOS 端实现

这段代码通过 EventChannel 实现了 iOS 应用中加速度传感器数据的实时传递。iOS端使用 CMMotionManager 监听加速度数据,并在数据更新时将 X、Y、Z 轴的加速度值传递给 Flutter 应用。当 Flutter 端开始监听时,传感器数据会持续发送,提供实时反馈。

swift 复制代码
import Flutter
import UIKit
import CoreMotion  // 导入 CoreMotion 框架,用于处理传感器数据

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    private let motionManager = CMMotionManager()  // 创建 CMMotionManager 实例,用于获取加速度数据

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
        
        // 创建 EventChannel,用于与 Flutter 端通信
        let eventChannel = FlutterEventChannel(name: "com.example.accelerometer", binaryMessenger: controller.binaryMessenger)

        // 使用 StreamHandler 处理 Flutter 的数据监听请求
        eventChannel.setStreamHandler(SensorStreamHandler())

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    // 自定义的 StreamHandler 类,用于监听和传递加速度数据
    class SensorStreamHandler: NSObject, FlutterStreamHandler {
        // 监听 Flutter 端的请求,当 Flutter 开始监听时会调用此方法
        func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
            let motionManager = CMMotionManager()  // 创建一个 CMMotionManager 实例
            
            // 检查加速度传感器是否可用
            if motionManager.isAccelerometerAvailable {
                // 设置传感器数据更新的时间间隔(0.1 秒)
                motionManager.accelerometerUpdateInterval = 0.1
                
                // 开始监听加速度数据,并将数据通过事件回调传回 Flutter
                motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { (data, error) in
                    if let accelerometerData = data {
                        // 获取 x、y、z 轴的加速度数据
                        let x = accelerometerData.acceleration.x
                        let y = accelerometerData.acceleration.y
                        let z = accelerometerData.acceleration.z
                        
                        // 将加速度数据格式化成字符串,并通过 events 回调发送给 Flutter
                        events("x: \(x), y: \(y), z: \(z)")
                    }
                }
            }
            return nil
        }

        // 当 Flutter 停止监听时调用此方法
        func onCancel(withArguments arguments: Any?) -> FlutterError? {
            return nil
        }
    }
}

方案三 使用Basic Message Channel

3.1 BasicMessageChannel介绍

BasicMessageChannel 是 Flutter 提供的一种通信机制,适用于在 Flutter 和原生平台之间发送和接收复杂的数据格式,如 JSON 或二进制数据。与 MethodChannel 不同,BasicMessageChannel 更加灵活,支持双向通信,但不提供结果回调功能。

适用场景--需要在 Flutter 和原生间频繁交换复杂数据结构时,例如:

  • 实时聊天应用中的消息传递
  • 自定义数据处理或转码

3.2 创建步骤

  1. 导入 Flutter 服务包: 首先,需要导入 package:flutter/services.dart 包以使用 BasicMessageChannel
  2. 创建 BasicMessageChannel: 定义一个 BasicMessageChannel 实例,并指定通道名称和编码器。
  3. 发送消息: 使用 send 方法可以发送消息到原生端
  4. 接收消息: 使用 setMessageHandler 设置消息处理器,以便接收从原生端发送的消息

3.3 使用示例:Flutter与原生互相发送JSON消息

Flutter 端实现

使用 BasicMessageChannel 在 Flutter 和原生平台之间传递 JSON 数据。通过发送和接收 JSON 消息,Flutter 应用可以与原生组件进行高效的双向通信,适用于需要传递复杂数据结构的场景。

dart 复制代码
import 'package:flutter/services.dart';
import 'dart:convert';

const messageChannel = BasicMessageChannel('com.example.jsonchannel', StringCodec());

void sendJsonMessageToNative() {
  // 创建要发送的 JSON 数据
  Map<String, dynamic> jsonMessage = {
    'name': 'Flutter',
    'message': 'Hello from Flutter'
  };

  // 将 JSON 数据转换为字符串并发送到原生
  messageChannel.send(json.encode(jsonMessage));
}

void receiveJsonMessageFromNative() {
  // 接收来自原生的消息
  messageChannel.setMessageHandler((message) async {
    Map<String, dynamic> receivedData = json.decode(message);
    print('Received from native: ${receivedData['message']}');
    return json.encode({'status': 'success', 'response': 'Received your message!'});
  });
}

3.2. Android 端实现

在 Android 原生端使用 BasicMessageChannel 与 Flutter 进行双向 JSON 数据通信。它实现了接收、处理和响应来自 Flutter 的消息,以及向 Flutter 发送消息的功能。通过这种方式,Flutter 和原生应用能够有效地进行数据交互,适用于需要频繁通信的场景

kotlin 复制代码
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.StringCodec
import org.json.JSONObject

class MainActivity : FlutterActivity() {
    private val CHANNEL = "com.example.jsonchannel"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val messageChannel = BasicMessageChannel(flutterEngine!!.dartExecutor.binaryMessenger, CHANNEL, StringCodec())

        // 接收来自 Flutter 的消息
        messageChannel.setMessageHandler { message, reply ->
            val jsonObject = JSONObject(message)
            val name = jsonObject.getString("name")
            val msg = jsonObject.getString("message")
            println("Received from Flutter: $msg")

            // 回复消息给 Flutter
            val response = JSONObject()
            response.put("status", "success")
            response.put("message", "Hello from Android, $name")
            reply.reply(response.toString())
        }

        // 发送消息到 Flutter(可选)
        val initialMessage = JSONObject()
        initialMessage.put("message", "Initial message from Android")
        messageChannel.send(initialMessage.toString())
    }
}

3.3 iOS 端实现

在 iOS 原生端使用 FlutterBasicMessageChannel 接收来自 Flutter 的 JSON 数据并进行处理,同时返回响应。实现了 Flutter 与原生平台之间的双向通信,允许交换复杂的数据结构。

swift 复制代码
import Flutter
import UIKit

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        let messageChannel = FlutterBasicMessageChannel(name: "com.example.jsonchannel",
                                                        binaryMessenger: controller.binaryMessenger,
                                                        codec: FlutterStringCodec.sharedInstance())

        // 接收来自 Flutter 的消息
        messageChannel.setMessageHandler { (message, reply) in
            if let jsonString = message as? String,
               let data = jsonString.data(using: .utf8),
               let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
               let name = json["name"] as? String,
               let msg = json["message"] as? String {
                print("Received from Flutter: \(msg)")
                
                // 回复消息给 Flutter
                let response: [String: Any] = ["status": "success", "message": "Hello from iOS, \(name)"]
                if let responseData = try? JSONSerialization.data(withJSONObject: response, options: []),
                   let responseString = String(data: responseData, encoding: .utf8) {
                    reply(responseString)
                }
            }
        }

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

方案四 Platform View

Platform View 是 Flutter 提供的一种机制,允许开发者在 Flutter 应用中嵌入原生平台的视图组件。这个方法非常适合需要使用原生控件而Flutter本身不提供类似控件的场景,比如嵌入WebView、地图组件或原生广告视图。

使用举例:嵌入原生WebView到Flutter

Flutter端代码

在 Flutter 中嵌入原生的 WebView 组件,加载指定的网页,并通过 PlatformViewLink 和原生代码进行交互。这种方式可以让 Flutter充分利用原生平台的特性和组件。

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class WebViewExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Embedded WebView'),
      ),
      body: Center(
        child: Container(
          width: 300,  // 设置 WebView 的宽度
          height: 400, // 设置 WebView 的高度
          child: PlatformViewLink(
            viewType: 'my_webview', // 定义视图类型
            onCreatePlatformView: (PlatformViewCreationParams params) {
              // 创建原生 WebView
              return PlatformViewsService.initSurfaceAndroidView(
                id: params.id,
                viewType: 'my_webview',
                layoutDirection: TextDirection.ltr,
                creationParams: <String, dynamic>{
                  'url': 'https://www.example.com', // 传递的参数(网页地址)
                },
                creationParamsCodec: const StandardMessageCodec(),
              )
                ..addOnPlatformViewCreated(params.onPlatformViewCreated);
            },
            // 在 WebView 创建前的占位符
            child: const Text('Waiting for the WebView...'),
          ),
        ),
      ),
    );
  }
}

Android端代码

这段代码实现了Flutter应用中的 WebView 集成,允许 Flutter 调用 Android 原生的 WebView 组件。主要步骤包括:

  1. 主活动注册视图工厂:在 MainActivity 中注册 MyWebViewFactory,使 Flutter 知道如何创建 WebView。
  2. 自定义 WebView:创建 MyWebView 类实现 PlatformView,设置 WebView 属性并加载 URL。
  3. 工厂类:通过 MyWebViewFactory 创建 MyWebView 实例并传递参数。

这样,Flutter 应用可以在界面中嵌入Android端原生的 WebView,提供更加灵活的网页浏览体验。

kotlin 复制代码
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.common.MethodChannel
import org.json.JSONObject

class MainActivity : FlutterActivity() {
    private val CHANNEL = "my_webview"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 注册 Platform View
        val viewFactory = MyWebViewFactory()
        registrar().platformViewRegistry().registerViewFactory(CHANNEL, viewFactory)
    }
}

// 自定义的 WebView 类
class MyWebView(context: Context, url: String) : PlatformView {
    private val webView: WebView = WebView(context).apply {
        // 设置 WebView 的属性
        settings.javaScriptEnabled = true // 启用 JavaScript
        webViewClient = WebViewClient() // 设置 WebView 客户端
        loadUrl(url) // 加载传入的 URL
    }

    // 返回 WebView 视图
    override fun getView(): View {
        return webView
    }

    // 清理资源
    override fun dispose() {
        webView.destroy() // 释放 WebView
    }
}

// WebView 工厂类,用于创建 MyWebView 实例
class MyWebViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
    override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
        // 解析传入的参数,获取 URL
        val jsonArgs = JSONObject(args.toString())
        val url = jsonArgs.getString("url")
        return MyWebView(context, url) // 返回 MyWebView 实例
    }
}

iOS 端代码

创建了一个自定义的原生 WebView,允许 Flutter 应用通过 Platform View 嵌入原生的网页视图。它利用 WKWebView 加载网页,并通过 FlutterPlatformViewFactory 提供的接口将这个视图集成到 Flutter 界面中。这使得 Flutter 开发者能够在应用中利用原生的网页展示能力。

swift 复制代码
import UIKit
import Flutter
import WebKit  // 导入 WebKit 框架,用于加载网页

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller: FlutterViewController = window?.rootViewController as! FlutterViewController

        // 注册 Platform View
        let webViewFactory = MyWebViewFactory()
        registrar(forPlugin: "my_webview").register(webViewFactory, withId: "my_webview")

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

// 自定义的 WKWebView 类
class MyWebView: NSObject, FlutterPlatformView {
    private var webView: WKWebView

    init(frame: CGRect, viewIdentifier: Int64, arguments: Any?) {
        webView = WKWebView(frame: frame) // 创建 WKWebView 实例
        if let args = arguments as? [String: Any],
           let urlString = args["url"] as? String,
           let url = URL(string: urlString) {
            // 加载传入的 URL
            let request = URLRequest(url: url)
            webView.load(request)
        }
        super.init()
    }

    // 返回 WKWebView 视图
    func view() -> UIView {
        return webView
    }
}

// WebView 工厂类,用于创建 MyWebView 实例
class MyWebViewFactory: NSObject, FlutterPlatformViewFactory {
    func create(
        withFrame frame: CGRect,
        viewIdentifier: Int64,
        arguments: Any?
    ) -> FlutterPlatformView {
        return MyWebView(frame: frame, viewIdentifier: viewIdentifier, arguments: arguments) // 返回 MyWebView 实例
    }

    func create(withFrame frame: CGRect, viewIdentifier: Int64, arguments: Any?) -> FlutterPlatformView {
        return MyWebView(frame: frame, viewIdentifier: viewIdentifier, arguments: arguments)
    }
}

方案五 Federated Plugin Architecture

这是Flutter社区推荐的插件架构设计模式,主要适用于那些需要兼容多个平台(如Android、iOS、Web、桌面端)的场景。在Federated Plugin架构中,每个平台的实现都被拆分成独立的插件包,从而可以为不同平台提供定制化的实现。Flutter与每个平台的交互逻辑可以分离,实现跨平台的统一接口管理。

Federated Plugin Architecture 示例:获取电池电量

以下是一个使用 Federated Plugin Architecture 创建的示例,能够在 Flutter 中获取设备的电池电量。

  1. 定义一个统一的Dart接口插件包,例如battery_interface,并包含跨平台API的声明:
dart 复制代码
abstract class BatteryPlatform {
  Future<int> getBatteryLevel();
}
  1. 针对不同平台(如battery_androidbattery_ios)实现具体的API调用方法。每个平台的实现会实现同样的BatteryPlatform接口,并且可以各自使用MethodChannel与其平台通信。

  2. 在Flutter中引入这些平台插件,通过统一接口调用:

dart 复制代码
import 'battery_interface.dart';  // 统一的Dart接口

class Battery {
  Future<int> getBatteryLevel() {
    return BatteryPlatform.instance.getBatteryLevel();
  }
}

方案六 直接调用原生插件

Flutter生态系统中有很多开源的第三方插件已经集成了与原生平台的交互功能,比如访问相机、文件系统、传感器等。开发者可以通过pub.dev直接引入这些插件,而不需要自己编写原生代码。

举例:使用url_launcher插件在浏览器中打开链接

dart 复制代码
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('URL Launcher Example')),
        body: Center(
          child: ElevatedButton(
            onPressed: _launchURL,
            child: Text('Open Flutter Website'),
          ),
        ),
      ),
    );
  }

  void _launchURL() async {
    const url = 'https://flutter.dev';
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }
}

总结

Flutter与原生平台的交互方式非常多样化,包括:

  1. MethodChannel:单次请求-响应模式,常用于功能调用。
  2. EventChannel:用于持续传输数据流,常用于传感器数据。
  3. BasicMessageChannel:支持双向通信,适合复杂数据的传递。
  4. Platform View:嵌入原生UI组件,显示复杂的原生视图。
  5. Federated Plugin Architecture:跨平台插件的设计模式,便于多平台兼容。
  6. 使用现有的第三方插件:如url_launchercamera等。

选择哪种方式取决于具体的功能需求和交互的复杂程度。

相关推荐
学不会•2 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
活宝小娜4 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点4 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow4 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o4 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic5 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā5 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年7 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder7 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727577 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架