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;
    });
  }
}
相关推荐
奔跑吧 android几秒前
【android bluetooth 框架分析 02】【Module详解 6】【StorageModule 模块介绍】
android·bluetooth·bt·aosp13·storagemodule
田一一一4 小时前
Android framework 中间件开发(三)
android·中间件·framework·jni
只可远观6 小时前
Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件
前端·flutter
周胡杰6 小时前
组件导航 (HMRouter)+flutter项目搭建-混合开发+分栏效果
前端·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
肥肥呀呀呀6 小时前
flutter Stream 有哪两种订阅模式。
flutter
androidwork8 小时前
掌握 Kotlin Android 单元测试:MockK 框架深度实践指南
android·kotlin
田一一一8 小时前
Android framework 中间件开发(二)
android·中间件·framework
追随远方9 小时前
FFmpeg在Android开发中的核心价值是什么?
android·ffmpeg
神探阿航10 小时前
HNUST湖南科技大学-安卓Android期中复习
android·安卓·hnust
千里马-horse12 小时前
android vlc播放rtsp
android·media·rtsp·mediaplayer·vlc