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;
    });
  }
}
相关推荐
openinstall全渠道统计3 小时前
免填邀请码工具:赋能六大核心场景,重构App增长新模型
android·ios·harmonyos
双鱼大猫3 小时前
一句话说透Android里面的ServiceManager的注册服务
android
双鱼大猫3 小时前
一句话说透Android里面的查找服务
android
双鱼大猫3 小时前
一句话说透Android里面的SystemServer进程的作用
android
双鱼大猫3 小时前
一句话说透Android里面的View的绘制流程和实现原理
android
双鱼大猫4 小时前
一句话说透Android里面的Window的内部机制
android
双鱼大猫4 小时前
一句话说透Android里面的为什么要设计Window?
android
双鱼大猫4 小时前
一句话说透Android里面的主线程创建时机,frameworks层面分析
android
苏金标5 小时前
android 快速定位当前页面
android
Zsnoin能7 小时前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter