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;
    });
  }
}
相关推荐
南村群童欺我老无力.2 小时前
Flutter 框架跨平台鸿蒙开发 - 开发双人对战五子棋游戏
flutter·游戏·华为·typescript·harmonyos
stevenzqzq2 小时前
Android 协程 Channel 菜鸟教程
android·channel
夜雨声烦丿2 小时前
Flutter 框架跨平台鸿蒙开发 - 消消乐游戏开发教程
flutter·游戏·华为·harmonyos
遗悲风3 小时前
PHP伪协议全面解析:原理、常用场景、攻防实战与安全防护
android·安全·php
撩得Android一次心动3 小时前
Android Lifecycle 全面解析:掌握生命周期管理的艺术(源码篇)
android·lifecycle
stevenzqzq3 小时前
android fow 限流
android·限流·flow
夜雨声烦丿3 小时前
Flutter 框架跨平台鸿蒙开发 - 数独求解器开发教程
flutter·游戏·华为·harmonyos
世人万千丶3 小时前
Day 5: Flutter 框架文件系统交互 - 鸿蒙沙盒机制下的文件读写与安全策略
学习·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
南村群童欺我老无力.4 小时前
Flutter 框架跨平台鸿蒙开发 - 白噪音助眠应用开发指南
flutter·华为·harmonyos
冬奇Lab4 小时前
Android 15 显示子系统深度解析(二):图形缓冲区管理与HWC硬件合成
android