一、Flutter 与原生页面交互的重要性和应用场景
Flutter 是一个由 Google 开发的开源框架,用于创建跨平台的移动、Web 和桌面应用程序。Flutter 允许开发者利用一套代码库为 Android 和 iOS 等平台构建美观、高性能的应用程序。然而,尽管 Flutter 提供了丰富的组件和库,某些环境下,开发者大概仍必要利用平台特有的功能或集成现有的原生页面和服务。
在以下场景中,Flutter 与原生页面交互显得尤为重要:
- 利用现有原生代码:在项目中大概已经存在大量的原生代码,重新用 Flutter 实现大概不现实或本钱过高。
- 调用平台特有的API:一些平台特有的API(如支付、地图等)只能通过原生代码访问。
- 集成第三方原生库:一些第三方库只提供原生的接口,必要通过原生代码来集成。
- 性能关键任务:对于一些性能敏感的任务,原生代码往往可以提供更好的性能表现。
- 遵循平台筹划规范:偶尔为了保持应用的一致性,必要利用原生组件来服从特定平台的筹划规范。
通过与原生页面的交互,Flutter 开发者可以充分利用平台的本事,同时保持应用的流畅性和用户体验的一致性。
二、根本概念
1.平台通道(Platform Channels)先容
平台通道是Flutter中一个强大的机制,它允许Flutter与宿主平台(如Android和iOS)举行数据和任务的双向通讯。这种机制办理了Flutter无法直接调用平台特定API的限定,使得Flutter应用能够实现原平生台的功能。
(1).通道的工作原理
平台通道通过利用一个消息通报的系统工作,该系统包罗以下三个关键概念:
- 消息:通报的数据单元,可以是简朴的字符串、数字或者更复杂的序列化数据结构。
- 编解码器:负责消息的序列化与反序列化。Flutter提供了几种尺度的编解码器,例如JSONMessageCodec、StandardMessageCodec、StringCodec和BinaryCodec。
- 通道:毗连Flutter和原平生台代码的通讯通道。
(2).通道的种类
Flutter提供了三种类型的平台通道,每种通道都适合差别的应用场景:
MethodChannel用于通报方法调用及其响应。方法调用是单次的,通常用于执行原生操作并获取结果。
EventChannel用于创建一个数据流,它允许原生代码发送连续的变乱数据给Flutter。这适合于监听原生端的变乱或数据变化,如传感器数据或用户位置更新。
BasicMessageChannel用于通报没有固定响应的消息,支持自定义的编解码器。这适合于双向通讯,比如状态同步或数据共享。
(3).序列化机制
由于消息必要在Flutter和原生代码之间通报,以是它们必要被序列化(转换为字节序列)和反序列化。Flutter框架提供了几个内置的编解码器来处理惩罚常见的数据类型,开发者也可以自定义编解码器。
(4).平台通道的利用
利用平台通道时,通常必要在Flutter端和原生端同时举行代码编写。
以下是一个简朴的MethodChannel利用示例:
Flutter端
- import 'package:flutter/services.dart';
- class BatteryLevel {
- static const MethodChannel _channel =
- MethodChannel('com.example.battery');
- static Future<int> getBatteryLevel() async {
- final int batteryLevel = await _channel.invokeMethod('getBatteryLevel');
- return batteryLevel;
- }
- }
复制代码 Android端(Kotlin)
- class MainActivity: FlutterActivity() {
- private val CHANNEL = "com.example.battery"
- override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
- super.configureFlutterEngine(flutterEngine)
- MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
- call, result ->
- if (call.method == "getBatteryLevel") {
- val batteryLevel = getBatteryLevel()
- if (batteryLevel != -1) {
- result.success(batteryLevel)
- } else {
- result.error("UNAVAILABLE", "Battery level not available.", null)
- }
- } else {
- result.notImplemented()
- }
- }
- }
- private fun getBatteryLevel(): Int {
- val batteryLevel: Int
- if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
- val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
- batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
- } else {
- val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
- batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
- }
- return batteryLevel
- }
- }
复制代码 iOS端(Swift)
- import Flutter
- import UIKit
- @UIApplicationMain
- @objc class AppDelegate: FlutterAppDelegate {
- private let batteryChannel = "com.example.battery"
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
-
- let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
- let methodChannel = FlutterMethodChannel(name: batteryChannel,
- binaryMessenger: controller.binaryMessenger)
- methodChannel.setMethodCallHandler({
- [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
- // Handle battery messages.
- guard call.method == "getBatteryLevel" else {
- result(FlutterMethodNotImplemented)
- return
- }
- self?.receiveBatteryLevel(result: result)
- })
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
- private func receiveBatteryLevel(result: FlutterResult) {
- let device = UIDevice.current
- device.isBatteryMonitoringEnabled = true
- if device.batteryState == UIDevice.BatteryState.unknown {
- result(FlutterError(code: "UNAVAILABLE",
- message: "Battery level not available.",
- details: nil))
- } else {
- result(Int(device.batteryLevel * 100))
- }
- }
- }
复制代码
2.Flutter 平台通道比力及示例
在Flutter中,MethodChannel、EventChannel和BasicMessageChannel是三种差别的平台通道,用于实现Dart代码和原平生台代码之间的通讯。
MethodChannel
MethodChannel通常用于发送方法调用哀求,并接收单次响应。它适合于执行原生操作并获取结果的场景。
Flutter端
- import 'package:flutter/services.dart';
- // 创建MethodChannel实例
- const MethodChannel methodChannel = MethodChannel('com.example.channel/method');
- // 调用原生方法并获取电池电量
- Future<int> getBatteryLevel() async {
- try {
- // 调用原生平台的'getBatteryLevel'方法
- final int result = await methodChannel.invokeMethod('getBatteryLevel');
- return result;
- } on PlatformException catch (e) {
- // 处理异常情况
- return -1;
- }
- }
复制代码 Android端(Kotlin)
- import io.flutter.embedding.android.FlutterActivity
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.MethodChannel
- class MainActivity: FlutterActivity() {
- // 定义通道名称
- private val CHANNEL = "com.example.channel/method"
- override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
- super.configureFlutterEngine(flutterEngine)
- // 创建MethodChannel实例并设置方法调用处理器
- MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
- if (call.method == "getBatteryLevel") {
- // 执行获取电池电量的操作,并将结果返回给Flutter端
- val batteryLevel = getBatteryLevel()
- if (batteryLevel != -1) {
- result.success(batteryLevel)
- } else {
- result.error("UNAVAILABLE", "Battery level not available.", null)
- }
- } else {
- result.notImplemented()
- }
- }
- }
- // 模拟获取电池电量的函数
- private fun getBatteryLevel(): Int {
- // 这里只是一个示例,实际获取电池电量的方法与此不同
- return 100 // 假设电池电量是100%
- }
- }
复制代码 iOS端(Swift)
- import Flutter
- import UIKit
- @UIApplicationMain
- @objc class AppDelegate: FlutterAppDelegate {
- // 定义通道名称
- private let methodChannelName = "com.example.channel/method"
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
-
- guard let controller = window?.rootViewController as? FlutterViewController else {
- fatalError("rootViewController is not type FlutterViewController")
- }
-
- // 创建MethodChannel实例并设置方法调用处理器
- let methodChannel = FlutterMethodChannel(name: methodChannelName,
- binaryMessenger: controller.binaryMessenger)
- methodChannel.setMethodCallHandler({
- [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
- // 检查方法名并执行相应操作
- if call.method == "getBatteryLevel" {
- // 执行获取电池电量的操作,并将结果返回给Flutter端
- self?.receiveBatteryLevel(result: result)
- } else {
- result(FlutterMethodNotImplemented)
- }
- })
-
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
-
- private func receiveBatteryLevel(result: FlutterResult) {
- // 这里只是一个示例,实际获取电池电量的方法与此不同
- result(100) // 假设电池电量是100%
- }
- }
复制代码
EventChannel
EventChannel用于数据流(event streams),可以用来监听原生端发出的变乱。例如,原生端的传感器数据或数据库更新。
Flutter端
- import 'package:flutter/services.dart';
- // 创建EventChannel实例
- const EventChannel eventChannel = EventChannel('com.example.channel/stream');
- // 监听来自原生平台的事件流
- void listenToNativeEvents() {
- eventChannel.receiveBroadcastStream().listen((event) {
- // 处理原生平台发送的数据
- print('Received event: $event');
- }, onError: (error) {
- // 处理错误
- print('Received error: ${error.message}');
- });
- }
复制代码 Android端(Kotlin)
- import io.flutter.embedding.android.FlutterActivity
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.EventChannel
- import java.util.*
- class MainActivity : FlutterActivity() {
- // 定义通道名称
- private val CHANNEL = "com.example.channel/stream"
- // 模拟传感器数据流
- private val sensorDataStream = Timer()
- override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
- super.configureFlutterEngine(flutterEngine)
- // 创建EventChannel实例并设置事件流处理器
- EventChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setStreamHandler(
- object : EventChannel.StreamHandler {
- override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
- // 设置定时器,模拟传送传感器数据
- sensorDataStream.schedule(object : TimerTask() {
- override fun run() {
- // 模拟事件数据,这里发送当前时间戳
- events?.success(System.currentTimeMillis())
- }
- }, 0, 1000)
- }
- override fun onCancel(arguments: Any?) {
- // 取消事件流
- sensorDataStream.cancel()
- }
- }
- )
- }
- }
复制代码 iOS端(Swift)
- import Flutter
- import UIKit
- class StreamHandler: NSObject, FlutterStreamHandler {
- private var eventSink: FlutterEventSink?
- private var timer: Timer?
- func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
- eventSink = events
- // 设置定时器,模拟传送传感器数据
- timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
- // 模拟事件数据,这里发送当前时间戳
- events(Date().timeIntervalSince1970 * 1000)
- }
- return nil
- }
- func onCancel(withArguments arguments: Any?) -> FlutterError? {
- // 取消定时器
- timer?.invalidate()
- timer = nil
- eventSink = nil
- return nil
- }
- }
- @UIApplicationMain
- @objc class AppDelegate: FlutterAppDelegate {
- // 定义通道名称
- private let streamChannelName = "com.example.channel/stream"
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
-
- let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
- // 创建EventChannel实例并设置事件流处理器
- let eventChannel = FlutterEventChannel(name: streamChannelName, binaryMessenger: controller.binaryMessenger)
- let streamHandler = StreamHandler()
- eventChannel.setStreamHandler(streamHandler)
-
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
- }
复制代码
BasicMessageChannel
BasicMessageChannel 允许发送和接收字符串和半结构化信息,实用于自定义编码和协议,或者必要频仍通讯但数据量不大的场景。
Flutter端
- import 'package:flutter/services.dart';
- // 创建BasicMessageChannel实例
- BasicMessageChannel<String> basicMessageChannel =
- BasicMessageChannel<String>('com.example.channel/basic', StringCodec());
- // 向原生发送消息
- Future<void> sendMessage(String message) async {
- String response = await basicMessageChannel.send(message);
- print('Received response: $response');
- }
- // 接收来自原生的消息
- void receiveMessages() {
- basicMessageChannel.setMessageHandler((String message) async {
- print('Received message: $message');
- return 'Received!';
- });
- }
复制代码 Android端(Kotlin)
- import io.flutter.embedding.android.FlutterActivity
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.BasicMessageChannel
- import io.flutter.plugin.common.StringCodec
- class MainActivity: FlutterActivity() {
- private val CHANNEL = "com.example.channel/basic"
- override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
- super.configureFlutterEngine(flutterEngine)
- // 创建BasicMessageChannel实例
- val messageChannel = BasicMessageChannel<String>(flutterEngine.dartExecutor.binaryMessenger, CHANNEL, StringCodec.INSTANCE)
- // 设置消息接收器
- messageChannel.setMessageHandler { message, reply ->
- // 在这里处理从Flutter接收到的消息
- println("Received message: $message")
- // 回复消息给Flutter端
- reply.reply("Echo: $message")
- }
- }
- }
复制代码 iOS端(Swift)
- import Flutter
- import UIKit
- @UIApplicationMain
- @objc class AppDelegate: FlutterAppDelegate {
- private let basicMessageChannelName = "com.example.channel/basic"
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
-
- guard let controller = window?.rootViewController as? FlutterViewController else {
- fatalError("rootViewController is not type FlutterViewController")
- }
-
- // 创建BasicMessageChannel实例
- let messageChannel = FlutterBasicMessageChannel(name: basicMessageChannelName,
- binaryMessenger: controller.binaryMessenger,
- codec: FlutterStringCodec.sharedInstance())
-
- // 设置消息接收器
- messageChannel.setMessageHandler { (message: Any?, reply: FlutterReply) in
- // 在这里处理从Flutter接收到的消息
- if let messageStr = message as? String {
- print("Received message: \(messageStr)")
- // 回复消息给Flutter端
- reply("Echo: \(messageStr)")
- }
- }
-
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
- }
复制代码
3.异步编程在平台通道中的应用
在Flutter中,平台通道的调用通常是异步的,这意味着你可以利用async和await关键字来等待原生代码的执行结果而不会壅闭UI线程。
例如,利用MethodChannel哀求电池电量时,你通常会这样做:
- Future<void> _getBatteryLevel() async {
- String batteryLevel;
- try {
- final int result = await NativeBridge.getBatteryLevel();
- batteryLevel = 'Battery level at $result % .';
- } on PlatformException {
- batteryLevel = 'Failed to get battery level.';
- }
- print(batteryLevel);
- }
复制代码
三、设置平台通道
1.定义通道名
我们将设置一个名为 "com.example.channel/method" 的 MethodChannel 平台通道,这个通道将用于Flutter与原平生台(iOS和Android)之间的通讯。
2.在 Flutter端创建MethodChannel
在Flutter端,你必要创建一个 MethodChannel 实例,并通过它发送消息到原平生台。
- // 导入相关包
- import 'package:flutter/services.dart';
- // 定义通道名
- const platformChannel = MethodChannel('com.example.channel/method');
- // 调用原生方法
- Future<void> getNativeData() async {
- try {
- final String result = await platformChannel.invokeMethod('getNativeData');
- print('从原生平台获取的数据:$result');
- } on PlatformException catch (e) {
- print("调用原生平台方法失败:${e.message}");
- }
- }
复制代码 在上面的代码中,我们定义了一个方法通道名 "com.example.channel/method",并创建了 MethodChannel 实例。我们定义了一个名为 getNativeData 的函数,该函数调用原平生台的 'getNativeData' 方法,并处理惩罚来自原平生台的响应或异常。
3.在Android(Kotlin)端监听通道
在Android端,我们必要在 MainActivity 中设置 MethodChannel 并定义方法调用的处理惩罚逻辑。
- // 导入相关包
- import io.flutter.embedding.android.FlutterActivity
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.MethodCall
- import io.flutter.plugin.common.MethodChannel
- import io.flutter.plugin.common.MethodChannel.MethodCallHandler
- class MainActivity: FlutterActivity() {
- private val CHANNEL = "com.example.channel/method"
- override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
- super.configureFlutterEngine(flutterEngine)
- // 设置MethodChannel并监听来自Flutter的调用
- MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
- // 判断调用的方法名
- if (call.method == "getNativeData") {
- // 执行获取数据的逻辑
- val nativeData = "来自Android的数据"
- // 使用result对象发送结果回Flutter
- result.success(nativeData)
- } else {
- // 如果方法未实现,返回未实现的错误
- result.notImplemented()
- }
- }
- }
- }
复制代码 在这段代码中,我们在 MainActivity 类中创建了一个 MethodChannel 实例,然后设置了一个 MethodCallHandler 来监听来自Flutter的方法调用。当Flutter调用 'getNativeData' 方法时,我们返回一些模仿的数据给Flutter。
3.在iOS(Swift)端监听通道
在iOS端,我们在 AppDelegate 中设置 MethodChannel 并处理惩罚方法调用。
- import UIKit
- import Flutter
- @UIApplicationMain
- @objc class AppDelegate: FlutterAppDelegate {
- // 定义通道名 与Flutter端定义的通道名称保持一致
- private let channelName = "com.example.channel/method"
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
- guard let controller = window?.rootViewController as? FlutterViewController else {
- fatalError("rootViewController is not type FlutterViewController")
- }
- let channel = MethodChannel(name: channelName, binaryMessenger: controller.binaryMessenger)
- // 设置监听器以处理来自Flutter的方法调用
- channel.setMethodCallHandler {
- (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
- // 检查方法名以确定执行哪个原生方法
- if call.method == "getNativeData" {
- // 执行获取数据逻辑
- let nativeData = "来自iOS的数据"
- // 发送结果回Flutter
- result(nativeData)
- } else {
- // 如果方法未实现,返回未实现的错误
- result(FlutterMethodNotImplemented)
- }
- }
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
- }
复制代码 在这段Swift代码中,我们在 AppDelegate 类中同样定义了一个 MethodChannel,并设置了一个闭包作为 MethodCallHandler 来监听Flutter的方法调用。当Flutter哀求 'getNativeData' 方法时,我们返回一串模仿的数据。
四、跳转到原生页面
为了实现Flutter跳转到原生页面的功能,我们必要在三个平台上编写相应的代码:Flutter、iOS和Android。
Flutter端
在Flutter端,我们利用MethodChannel来发起跳转到原生页面的哀求。
- // 导入必要的包
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- void main() => runApp(MyApp());
- class MyApp extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- home: HomeScreen(),
- );
- }
- }
- class HomeScreen extends StatelessWidget {
- // 创建MethodChannel实例,通道名称要与原生端一致
- static const platformChannel = MethodChannel('com.example.channel/native');
- // 调用原生方法跳转到原生页面的函数
- Future<void> openNativeScreen() async {
- try {
- // 调用原生平台的openNativeScreen方法
- await platformChannel.invokeMethod('openNativeScreen');
- } on PlatformException catch (e) {
- // 如果跳转失败,捕获异常
- print("Failed to open native screen: ${e.message}");
- }
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Flutter Home Screen'),
- ),
- body: Center(
- child: ElevatedButton(
- onPressed: openNativeScreen, // 点击按钮时发起跳转
- child: Text('Go to Native Screen'),
- ),
- ),
- );
- }
- }
复制代码 在上面的Flutter代码中,我们定义了一个按钮,当用户点击这个按钮时,openNativeScreen函数会通过MethodChannel发起一个名为openNativeScreen的方法调用。
Android端(Kotlin)
在Android端,在MainActivity中监听MethodChannel,并对openNativeScreen方法调用举行处理惩罚,启动一个新的Activity。
- // MainActivity.kt
- import android.content.Intent
- import io.flutter.embedding.android.FlutterActivity
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.MethodChannel
- class MainActivity: FlutterActivity() {
- private val CHANNEL = "com.example.channel/native"
- override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
- super.configureFlutterEngine(flutterEngine)
- MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
- if (call.method == "openNativeScreen") {
- // 创建Intent并启动新的Activity
- val intent = Intent(this, NativeActivity::class.java)
- startActivity(intent)
- // 返回成功结果
- result.success(null)
- } else {
- result.notImplemented()
- }
- }
- }
- }
复制代码- // NativeActivity.kt
- import android.os.Bundle
- import androidx.appcompat.app.AppCompatActivity
- // 创建新的Activity用于展示原生页面
- class NativeActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // 设置原生页面的布局
- setContentView(R.layout.activity_native)
- }
- }
复制代码 在Android项目中,我们还必要在AndroidManifest.xml中注册NativeActivity。
- <activity android:name=".NativeActivity">
- <!-- 配置和其他Activity相关的属性 -->
- </activity>
复制代码
iOS端(Swift)
在iOS端,在AppDelegate中监听MethodChannel,并对openNativeScreen方法调用举行处理惩罚,启动一个新的UIViewController。
- import UIKit
- import Flutter
- @UIApplicationMain
- @objc class AppDelegate: FlutterAppDelegate {
- private let channelName = "com.example.channel/native"
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
- if let controller = window?.rootViewController as? FlutterViewController {
- let channel = FlutterMethodChannel(name: channelName, binaryMessenger: controller.binaryMessenger)
- channel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
- if call.method == "openNativeScreen" {
- // 实现UIViewController的跳转逻辑
- self.openNativeScreen(from: controller)
- result(nil)
- } else {
- result(FlutterMethodNotImplemented)
- }
- }
- }
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
- private func openNativeScreen(from flutterViewController: FlutterViewController) {
- // 创建原生页面的UIViewController
- let nativeViewController = NativeViewController()
- // 从当前FlutterViewController进行页面跳转
- flutterViewController.present(nativeViewController, animated: true, completion: nil)
- }
- }
复制代码- // NativeViewController.swift
- import UIKit
- // 创建一个新的UIViewController子类作为原生页面
- class NativeViewController: UIViewController {
- override func viewDidLoad() {
- super.viewDidLoad()
- // 设置原生页面的属性,例如背景色
- view.backgroundColor = UIColor.white
- }
- }
复制代码
五、从 Flutter 传值给原生页面并得到响应
为了实现从Flutter应用通报数据到原生页面,并在原生页面中接收息争析这些数据,我们必要分别在Flutter、Android和iOS端编写代码。
Flutter端发送数据
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'dart:convert';
- void main() {
- runApp(MyApp());
- }
- class MyApp extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- home: HomeScreen(),
- );
- }
- }
- class HomeScreen extends StatelessWidget {
- // 创建MethodChannel实例,通道名称要与原生端一致
- static const platformChannel = MethodChannel('com.example.channel/transfer');
- // 向原生页面发送简单数据
- Future<void> sendSimpleData() async {
- try {
- final String response = await platformChannel.invokeMethod('sendSimpleData', {'message': 'Hello from Flutter!'});
- print(response); // 打印原生页面返回的响应
- } on PlatformException catch (e) {
- print("Failed to send simple data: ${e.message}");
- }
- }
- // 向原生页面发送复杂数据(如JSON)
- Future<void> sendComplexData() async {
- try {
- final Map<String, dynamic> complexData = {
- 'user': {
- 'id': 1,
- 'name': 'John Doe',
- 'email': 'johndoe@example.com',
- }
- };
- final String response = await platformChannel.invokeMethod('sendComplexData', {'data': json.encode(complexData)});
- print(response); // 打印原生页面返回的响应
- } on PlatformException catch (e) {
- print("Failed to send complex data: ${e.message}");
- }
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Flutter to Native Data Transfer'),
- ),
- body: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: <Widget>[
- ElevatedButton(
- onPressed: sendSimpleData,
- child: Text('Send Simple Data'),
- ),
- ElevatedButton(
- onPressed: sendComplexData,
- child: Text('Send Complex Data'),
- ),
- ],
- ),
- ),
- );
- }
- }
复制代码 在Flutter端,我们创建了两个函数sendSimpleData和sendComplexData,用于发送简朴和复杂数据。数据通过MethodChannel的invokeMethod函数发送到原生端,同时可以从原生端接收响应。
Android端(Kotlin)
在Android端,我们监听MethodChannel,并利用Gson库来解析JSON格式的复杂数据。
- // MainActivity.kt
- import android.os.Bundle
- import androidx.appcompat.app.AppCompatActivity
- import io.flutter.embedding.android.FlutterActivity
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.MethodChannel
- import com.google.gson.Gson
- import com.google.gson.reflect.TypeToken
- class MainActivity: FlutterActivity() {
- private val CHANNEL = "com.example.channel/transfer"
- override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
- super.configureFlutterEngine(flutterEngine)
- MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
- when (call.method) {
- "sendSimpleData" -> {
- val message = call.argument<String>("message")
- // 使用传递的简单数据
- // ...
- // 发送响应回Flutter端
- result.success("Received simple data: $message")
- }
- "sendComplexData" -> {
- val data = call.argument<String>("data")
- val type = object : TypeToken<Map<String, Any>>() {}.type
- val complexData: Map<String, Any> = Gson().fromJson(data, type)
- // 使用传递的复杂数据
- // ...
- // 发送响应回Flutter端
- result.success("Received complex data")
- }
- else -> result.notImplemented()
- }
- }
- }
- }
复制代码
iOS端(Swift)
在iOS端,我们监听MethodChannel,并利用Swift的Codable协议或JSONSerialization来解析JSON格式的复杂数据。
- // AppDelegate.swift
- import UIKit
- import Flutter
- @UIApplicationMain
- @objc class AppDelegate: FlutterAppDelegate {
- private let channelName = "com.example.channel/transfer"
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions:[UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
- guard let controller = window?.rootViewController as? FlutterViewController else {
- fatalError("rootViewController is not type FlutterViewController")
- }
- let channel = FlutterMethodChannel(name: channelName,
- binaryMessenger: controller.binaryMessenger)
- channel.setMethodCallHandler { [weak self] (call, result) in
- switch call.method {
- case "sendSimpleData":
- if let message = call.arguments as? String {
- // 使用传递的简单数据
- // ...
-
- // 发送响应回Flutter端
- result("Received simple data: \(message)")
- } else {
- result(FlutterError(code: "INVALID_ARGUMENT", message: "Expected a string argument", details: nil))
- }
- case "sendComplexData":
- if let jsonString = call.arguments as? String,
- let data = jsonString.data(using: .utf8) {
- do {
- // 使用Codable进行解析
- let user = try JSONDecoder().decode(User.self, from: data)
- // 使用传递的复杂数据
- // ...
-
- // 发送响应回Flutter端
- result("Received complex data")
- } catch {
- result(FlutterError(code: "JSON_PARSE_ERROR", message: "Error parsing JSON", details: nil))
- }
- } else {
- result(FlutterError(code: "INVALID_ARGUMENT", message: "Expected a JSON string argument", details: nil))
- }
- default:
- result(FlutterMethodNotImplemented)
- }
- }
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
- }
- // 定义User结构体以匹配传递的复杂数据结构
- struct User: Codable {
- var id: Int
- var name: String
- var email: String
- }
复制代码
六、从原生页面回传数据到 Flutter
当必要从原生页面回传数据到Flutter时,有两种常见的方法:利用MethodChannel或者利用ActivityResult(Android端)/利用委托(delegate)/闭包(closures)(iOS端)。
- 利用MethodChannel回传数据:此方法实用于任何时候原生代码必要自动发送数据到Flutter端的环境,不仅限于页面返回时。Flutter端通过MethodChannel与原生代码通讯,可以接收来自原生端的数据。
- 利用ActivityResult(Android)/闭包和协议委托(iOS):此方法一样平常用于原生页面关闭时,将数据回传到Flutter端。在Android上,可以通过setResult方法和Intent返回数据给前一个Activity。在iOS上,可以通过署理模式或闭包将数据回传给之前的控制器。
在Flutter端,我们必要设置好MethodChannel监听原生端发来的数据,或者在启动原生页面时等待结果。
方式一:远程过程调用
Flutter端设置了MethodChannel监听并处理惩罚原生端的调用,当原生代码处理惩罚完毕后,通过相同的MethodChannel返回结果。这是一种典范的RPC(远程过程调用)模式。
- 单次哀求-响应模式:Flutter发起调用,原生端返回数据,通讯完成。
- Flutter端自动哀求:Flutter通过invokeMethod自动哀求原生数据。
- 异步等待原生端响应:Flutter调用后利用await关键字等待原生端完成操作并返回结果。
- 实用场景:当Flutter必要原生端某个特定操作的结果时利用,比如获取设备信息、处理惩罚完毕的数据等。
Flutter端接收返回数据
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- class HomeScreen extends StatefulWidget {
- @override
- _HomeScreenState createState() => _HomeScreenState();
- }
- class _HomeScreenState extends State<HomeScreen> {
- // 创建一个MethodChannel,用于与原生平台通信
- static const platformChannel = MethodChannel('com.example.channel/transfer');
- // 用于显示从原生平台接收到的数据
- String _dataFromNative = 'No data';
- @override
- void initState() {
- super.initState();
- // 在 initState 中设置监听器以处理原生平台发来的方法调用
- platformChannel.setMethodCallHandler(_handleMethodCall);
- }
- // 处理从原生平台接收到的方法调用
- Future<dynamic> _handleMethodCall(MethodCall call) async {
- switch (call.method) {
- case 'onDataReturn':
- // 当原生平台返回数据时,更新状态以显示数据
- setState(() {
- _dataFromNative = call.arguments;
- });
- break;
- default:
- throw MissingPluginException('notImplemented');
- }
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Flutter Native Data Return'),
- ),
- body: Center(
- // 显示从原生端接收到的数据
- child: Text(_dataFromNative),
- ),
- );
- }
- }
复制代码 Android端(Kotlin)设置返回数据
在Android端,你可以在MainActivity中创建一个按钮,并在点击变乱中利用MethodChannel向Flutter发送消息:
- // ...其他导入
- import io.flutter.embedding.android.FlutterActivity
- import io.flutter.plugin.common.MethodChannel
- class MainActivity: FlutterActivity() {
- // 与Flutter端相同的通道名称
- private val CHANNEL = "com.example.channel/transfer"
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // 初始化MethodChannel
- val channel = MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, CHANNEL)
- // ...其他代码
- // 用于向Flutter发送数据
- fun sendDataToFlutter() {
- // 使用MethodChannel调用Flutter端定义的方法,并传递数据
- channel.invokeMethod("onDataReturn", "这是来自原生平台的数据")
- }
- // 假设有个触发发送数据到Flutter的逻辑
- sendDataToFlutter()
- }
- }
复制代码
iOS端(Swift)设置返回数据
在iOS端,你可以在ViewController中为一个按钮,并在这个按钮回调中利用MethodChannel向Flutter发送消息:
- // ...其他导入
- import Flutter
- class ViewController: UIViewController {
- private var channel: FlutterMethodChannel?
- override func viewDidLoad() {
- super.viewDidLoad()
- // 初始化MethodChannel
- guard let controller = self as? FlutterViewController else {
- fatalError("ViewController is not type FlutterViewController")
- }
- channel = FlutterMethodChannel(name: "com.example.channel/transfer", binaryMessenger: controller.binaryMessenger)
- // ...其他代码
- // 用于向Flutter发送数据
- func sendDataToFlutter() {
- // 使用MethodChannel调用Flutter端定义的方法,并传递数据
- channel?.invokeMethod("onDataReturn", arguments: "这是来自原生平台的数据")
- }
- // 假设有个触发发送数据到Flutter的逻辑
- sendDataToFlutter()
- }
- }
复制代码
方式二:变乱订阅模式
是一种变乱订阅模式,Flutter端通过MethodChannel设置监听器,任何时候原生端都可以自动调用这个通道并发送数据到Flutter,而Flutter端则在_listener_方法中处理惩罚全部接收到的消息。
- 持续监听模式:Flutter监听原生端的方法调用,原生端可以在任何时刻自动发送消息。
- 原生端自动发送消息:原生代码在适当的时候(如某变乱发生后)自动调用MethodChannel向Flutter发送消息。
- 实用场景:对于原生端变乱的及时监听和处理惩罚,如位置更新、传感器数据等。
Flutter端接收返回数据
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- class HomeScreen extends StatelessWidget {
- // 创建MethodChannel实例
- static const platformChannel = MethodChannel('com.example.channel/transfer');
- // 调用原生页面并等待返回结果
- Future<void> _navigateAndDisplaySelection(BuildContext context) async {
- final result = await platformChannel.invokeMethod('startNativeView');
- // 使用返回结果更新UI或状态
- if (result != null) {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text('原生页面返回的数据: $result')),
- );
- }
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Flutter Native Data Return'),
- ),
- body: Center(
- child: ElevatedButton(
- onPressed: () => _navigateAndDisplaySelection(context),
- child: Text('打开原生页面'),
- ),
- ),
- );
- }
- }
复制代码 在上面的Flutter端代码中,我们通过platformChannel.invokeMethod调用原生端的方法打开一个原生页面,并利用await关键字等待异步结果。当原生页面关闭并返回数据时,我们可以通过result变量接收这个数据,并通过ScaffoldMessenger显示在一个SnackBar中。
Android端(Kotlin)设置返回数据
在Android端,我们可以利用setResult方法和Intent回传数据。
- // 原生页面Activity
- import android.app.Activity
- import android.content.Intent
- import android.os.Bundle
- class NativeActivity : Activity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // 设置布局、初始化等操作
- // ...
- // 假设这是一个按钮点击后触发的事件
- val button = findViewById<Button>(R.id.button_id)
- button.setOnClickListener {
- val returnIntent = Intent()
- returnIntent.putExtra("result_key", "这里是从原生返回的数据")
- setResult(Activity.RESULT_OK, returnIntent)
- finish() // 关闭当前原生页面,回传数据到Flutter
- }
- }
- }
复制代码 在Android原生端,你必要定义一个MethodChannel并监听startNativeView方法调用,然后打开一个新的Activity,并在关闭时设置返回结果:
- // MainActivity.kt (Android端)
- import io.flutter.embedding.android.FlutterActivity
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.MethodChannel
- class MainActivity: FlutterActivity() {
- private val CHANNEL = "com.example.channel/transfer"
- override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
- super.configureFlutterEngine(flutterEngine)
-
- MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
- call, result ->
- if (call.method == "startNativeView") {
- // 调用原生页面
- val intent = Intent(this, NativeActivity::class.java)
- startActivityForResult(intent, REQUEST_CODE)
- } else {
- result.notImplemented()
- }
- }
- }
- // 接收原生页面返回的结果
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
- val returnData = data?.getStringExtra("result_key") ?: "No data"
- // 使用MethodChannel返回数据到Flutter
- MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).invokeMethod("onDataReturn", returnData)
- }
- }
- companion object {
- const val REQUEST_CODE = 100
- }
- }
复制代码
iOS端(Swift)设置返回数据
在iOS端,我们可以利用闭包或者协议委托往返传数据。
- // 原生页面ViewController
- import UIKit
- class NativeViewController: UIViewController {
- // 定义闭包,用于回传数据
- var onReturnData: ((String) -> Void)?
- override func viewDidLoad() {
- super.viewDidLoad()
- // 设置布局、初始化等操作
- // ...
- let button = UIButton()
- button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
- }
- @objc func didTapButton() {
- // 当按钮被点击时,通过闭包传递数据回Flutter端
- onReturnData?("这里是从原生返回的数据")
- dismiss(animated: true, completion: nil) // 关闭当前页面
- }
- }
复制代码 在iOS原生端,你必要在Flutter与原生端之间创建一个MethodChannel,并监听Flutter端的调用,然后打开一个新的ViewController,并在关闭时回传数据:
- // AppDelegate.swift (iOS端)
- import UIKit
- import Flutter
- @UIApplicationMain
- @objc class AppDelegate: FlutterAppDelegate {
- private var flutterResult: FlutterResult?
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- if let controller = window?.rootViewController as? FlutterViewController {
- let channel = FlutterMethodChannel(name: "com.example.channel/transfer",
- binaryMessenger: controller.binaryMessenger)
- channel.setMethodCallHandler({
- [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
- // 保存FlutterResult回调
- self?.flutterResult = result
- // 检查是否是打开原生页面的调用
- if call.method == "startNativeView" {
- self?.showNativeView(from: controller)
- } else {
- result(FlutterMethodNotImplemented)
- }
- })
- }
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
- private func showNativeView(from controller: FlutterViewController) {
- let nativeViewController = NativeViewController()
- // 设置回调闭包,以便在ViewController关闭时返回数据
- nativeViewController.onReturnData = { [weak self] data in
- self?.flutterResult?(data)
- }
- // 展示原生ViewController
- controller.present(nativeViewController, animated: true, completion: nil)
- }
- }
复制代码
七、错误处理惩罚和异常安全
错误处理惩罚是编写健壮软件的重要组成部分。下面将详细先容怎样在Flutter、Android(Kotlin)、iOS(Swift)三个平台上举行错误处理惩罚和异常安全。
我们在Flutter端通过MethodChannel调用原生代码,并且在原生端利用try-catch(Kotlin)或do-try-catch(Swift)来捕捉大概发生的异常。假如捕捉到异常,原生端会通过MethodChannel.Result(Kotlin)或FlutterResult(Swift)将错误信息通报回Flutter。
异常处理惩罚机制确保了当原生代码执行出错时,Flutter端可以收到错误信息,并且可以根据错误信息做出相应的处理惩罚,从而提供更加健壮的用户体验。
Flutter端处理惩罚原生代码抛出的异常
在Flutter端,我们可以通过try-catch块来捕捉和处理惩罚通过MethodChannel调用原生代码时大概抛出的异常。
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- class ExceptionHandlingScreen extends StatefulWidget {
- @override
- _ExceptionHandlingScreenState createState() => _ExceptionHandlingScreenState();
- }
- class _ExceptionHandlingScreenState extends State<ExceptionHandlingScreen> {
- static const platformChannel = MethodChannel('com.example.channel/methods');
- String _result = 'No data';
- Future<void> _invokeNativeMethod(String methodName) async {
- try {
- final String result = await platformChannel.invokeMethod(methodName);
- setState(() {
- _result = result;
- });
- } on PlatformException catch (e) {
- // 捕获由原生代码抛出的异常
- setState(() {
- _result = "原生代码发生异常:${e.message}";
- });
- } catch (e) {
- // 捕获其他异常
- setState(() {
- _result = "未知异常:${e.toString()}";
- });
- }
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('异常处理示例'),
- ),
- body: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: <Widget>[
- ElevatedButton(
- onPressed: () => _invokeNativeMethod('someMethod'),
- child: Text('调用原生方法'),
- ),
- Padding(
- padding: const EdgeInsets.all(8.0),
- child: Text(_result),
- ),
- ],
- ),
- ),
- );
- }
- }
复制代码
Android端(Kotlin)
在Android(Kotlin)端,我们可以利用try-catch块来处理惩罚大概在执行方法调用时发生的异常,并通过MethodChannel.Result向Flutter端通报错误信息。
- // MainActivity.kt (Android端)
- import io.flutter.embedding.android.FlutterActivity
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.MethodChannel
- class MainActivity : FlutterActivity() {
- private val CHANNEL = "com.example.channel/methods"
- override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
- super.configureFlutterEngine(flutterEngine)
- MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
- try {
- when (call.method) {
- "someMethod" -> {
- // 你的原生方法处理
- result.success("从原生返回的数据")
- }
- else -> result.notImplemented()
- }
- } catch (e: Exception) {
- // 捕获异常并将错误返回到Flutter
- result.error("ERROR", "方法执行出错: ${e.localizedMessage}", null)
- }
- }
- }
- }
复制代码
iOS端(Swift)
在iOS(Swift)端,我们一样平常通过do-try-catch块来处理惩罚方法调用时大概抛出的异常,并通过FlutterResult通报错误信息给Flutter端。
- // AppDelegate.swift (iOS端)
- import UIKit
- import Flutter
- @UIApplicationMain
- @objc class AppDelegate: FlutterAppDelegate {
- private let channelName = "com.example.channel/methods"
- override func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
- ) -> Bool {
- guard let controller = window?.rootViewController as? FlutterViewController else {
- fatalError("rootViewController is not type FlutterViewController")
- }
- let channel = FlutterMethodChannel(name: channelName, binaryMessenger: controller.binaryMessenger)
- channel.setMethodCallHandler { (call, result) in
- // 检查方法名称并执行相应的原生代码
- if call.method == "someMethod" {
- do {
- // 尝试执行方法并捕获可能的异常
- let data = try self.someNativeMethod()
- result(data)
- } catch {
- // 如果有异常,将异常信息返回给Flutter
- result(FlutterError(code: "ERROR",
- message: "方法执行出错: \(error.localizedDescription)",
- details: nil))
- }
- } else {
- result(FlutterMethodNotImplemented)
- }
- }
- return super.application(application, didFinishLaunchingWithOptions: launchOptions)
- }
- private func someNativeMethod() throws -> String {
- // 这里是你的原生方法实现,可能会抛出异常
- // 例如,我们这里直接抛出一个异常来模拟错误情况
- throw NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "演示错误"])
- }
- }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |