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;
    });
  }
}
相关推荐
麻瓜生活睁不开眼7 小时前
Android16修改全局桌面视图边框四直角显示为弧边圆角
android·java·深度学习
AFinalStone10 小时前
Android 7系统休眠唤醒(六)唤醒全链路
android·电源管理·休眠唤醒
雨白11 小时前
深入理解 Kotlin 协程 (九):互通有无,解构 Channel 缓冲策略与底层无锁机制
android·kotlin
灯塔@kuaidao12 小时前
平台交叉编译名词解释与基础流程
android
__Witheart__13 小时前
3568 Android otg模式下adb热拔插不识别
android·adb·rockchip
明天…ling15 小时前
Upload-Labs (Pass1-Pass21) 完整通关思路与源码分析
android·网络安全·渗透测试·burpsuite·upload-labs·文件上传绕过·靶场复现
__Witheart__15 小时前
3568 Android ntp校时使用
android·rockchip
molihuan15 小时前
最新 spine 4.3 flutter 适配鸿蒙
flutter·动画·harmonyos·鸿蒙·spine
wddptwd2816 小时前
android studio 报错怎么处理 java.lang.NullPointerException
android·java·android studio
GitLqr16 小时前
StatefulWidget 里的隐形炸弹:为什么不要在 State 类中使用 context.mounted
flutter·面试·dart