flutter 开发笔记(九):原生桥接

作为跨端框架,Flutter 不可避免地需要与原生做交互。在开发过程中,有些功能需要直接调用原生平台提供的 API 或功能,例如访问设备的电池状态、相机、传感器等。为了实现这些功能,Flutter 提供了平台通道(MethodChannel),允许 Flutter 与 iOS 和 Android 原生代码进行通信。这种桥接机制使得开发者能够在跨平台应用中充分利用每个操作系统的特性和功能

接下来我们以获取电量为例,探讨下如何在 iOS 和 Android 平台上编写相应的原生代码,并通过平台通道将结果传递回 Flutter 层

Android

android 需要修改的文件是 MainActivity.kt,未更改的文件如下

kt 复制代码
package com.example.test_drive

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity()

更改后如下,做了以下的改动

  1. 固定 channel 名称 :通过 BASE_CHANNEL_NAMEBATTERY_CHANNEL_NAME 变量定义了通道名称,并通过字符串拼接方式构建了最终的 MethodChannel 名称
  2. 配置 MethodChannel:在 configureFlutterEngine 方法中,通过 flutterEngine.dartExecutor.binaryMessenger 创建了 MethodChannel 并设置了方法调用处理程序
  3. 获取电池电量 :在 getBatteryLevel 方法中,通过 BatteryManager 获取电池电量。如果获取成功,返回电量百分比;如果失败,返回 -1 作为失败的标志
kt 复制代码
package com.example.test_drive

import android.os.BatteryManager
import android.os.Build
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    private val BASE_CHANNEL_NAME = "com.example.testDrive"
    private val BATTERY_CHANNEL_NAME = "$BASE_CHANNEL_NAME/battery"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, BATTERY_CHANNEL_NAME).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 batteryManager = getSystemService(BATTERY_SERVICE) as BatteryManager
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
        } else {
            -1
        }
    }
}

iOS

iOS 需要修改的是 AppDelegate.swift 文件,其路径为 ios/Runner/AppDelegate.swift

未修改的文件如下

swift 复制代码
import Flutter
import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

更改后如下,做了以下的改动

  1. 获取 FlutterViewController 实例,参考 controller 相关代码
  2. 定义平台通道名称,参考 batteryChannel 相关代码
  3. 设置方法调用处理程序,参考 setMethodCallHandler 相关代码
  4. 新增方法:获取电池电量,参考 receiveBatteryLevel 相关代码
swift 复制代码
import Flutter
import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
    let baseChannelName = "com.example.testDrive"
    let batteryChannelName = "\(baseChannelName)/battery"
    let batteryChannel = FlutterMethodChannel(name: batteryChannelName,
                                              binaryMessenger: controller.binaryMessenger)

    batteryChannel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
      if call.method == "getBatteryLevel" {
        self.receiveBatteryLevel(result: result)
      } else {
        result(FlutterMethodNotImplemented)
      }
    }

    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 info unavailable",
                          details: nil))
    } else {
      result(Int(device.batteryLevel * 100))
    }
  }
}

如何使用

需要注意的是,更改了原生代码,无论是 android 还是 iOS,都需要重新 build 一次,而不是简单地 reload,我们创建一个 bridge_page.dart 页面,展示下如何调用

定义的 MethodChannel 要与在原生文件中定义的一致,通过 platform.invokeMethod('getBatteryLevel') 调用原生代码获取电池电量

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

class BridgePage extends StatefulWidget {
  const BridgePage({super.key});

  @override
  BridgePageState createState() => BridgePageState();
}

class BridgePageState extends State<BridgePage> {
  static const platform = MethodChannel('com.example.testDrive/battery');
  String _batteryLevel = 'Unknown battery level';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Bridge Page Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Battery Level: $_batteryLevel'),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _getBatteryLevel,
              child: const Text('Get Battery Level'),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level at $result %';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _batteryLevel = batteryLevel;
    });
  }
}
相关推荐
sun_weitao2 小时前
Flutter使用GestureDetector工具实现手势缩放效果
flutter
孑么6 小时前
GDPU Android移动应用 重点习题集
android·xml·java·okhttp·kotlin·android studio·webview
@OuYang8 小时前
Audio音频输出通道
android
ta叫我小白9 小时前
Kotlin 中 forEach 的 return@forEach 的使用误区
android·开发语言·kotlin
archko9 小时前
试用kotlin multiplatform
android·开发语言·kotlin
C4rpeDime10 小时前
当歌 - RSS 订阅分发平台开发
android
iOS阿玮11 小时前
聊聊正式接单第一天的感悟,以及Appstore合规化的看法。
ios
大雄野比12 小时前
UOS系统mysql服务安装
android·mysql·adb
yozyyyqls12 小时前
自定义Compose Pager实现电影卡片列表
android