在 Flutter App 中编写自定义平台特定代码[Method Channel]

前言
学习如何编写自定义平台代码,并将其连接到 flutter 应用程序中

Flutter 使用了一个灵活的系统,允许您使用直接与这些 API 协作的语言调用特定于平台的 API:
- 在 Android 上使用 Kotlin 或 Java
 - IOS 上的 Swift 或 Objective-C
 - Windows 下的 C + +
 - 在 macOS 上的 Objective-C
 - Linux 上的 C 语言
 
正文
下面的解释来自 Flutter 官方文档,解释 Method Channel 是如何工作的

我也将 应用 application 在官方文件的解释得到当前的设备电池电量给予更多的背景下,它是如何工作的
首先使用你喜欢的 IDE 创建一个 Flutter 应用程序
清除 Flutter Starter 应用程序并创建一个新的 dart 文件 home_screen. dart

在 home_screen. dart 内部创建一个新的状态完整的 widget ,返回一个现在的脚本框
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);
  
  State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
  
  Widget build(BuildContext context) {
    return Scaffold();
  }
}
然后导入那些库
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
请注意,与 Platfrom 的通信是异步的,所以我们将在这个应用程序的未来工作
创建一个字符串
String _batteryLevel = 'Unknown battery level.';
您的代码现在应该如下所示
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);
  
  State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
  String _batteryLevel = 'Unknown battery level.';
  
  Widget build(BuildContext context) {
    return Scaffold();
  }
}
然后在您的状态内创建一个未来的异步函数 getBatteryLevel
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;
  });
}
现在您将注意到平台中的一个错误,这是因为我们还没有定义它
在我们声明它之前,我们需要知道 Method Channel 必须有一个唯一的域,以便两端进行通信,因此它将是不可变的
返回您的状态并释放一个 methodChannel 对象
static const platform = MethodChannel('mediumExplain/battery');
然后,我们需要添加一些简单的用户界面交互,以获得电池电平
用此代码替换生成方法
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(),
    body: Column(
      children: [
        Text('Your battery level is ${_batteryLevel}'),
        ElevatedButton(
          onPressed: _getBatteryLevel,
          child: const Text('Get Battery Level'),
        ),
      ],
    ),
  );
}
现在是机器人的本机部分,
Android 配置 Kotlin
打开 android 应用程序目录
app -> src ->main->kotlin->MainActivity.kt
- 加上那些进口
 
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import androidx.annotation.NonNull
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
这些都是本地库从机器人联系电池电平 hteapi
然后添加这个变量,就像我们在 flutter 应用程序中创建的那样
class MainActivity: FlutterActivity() {
  private val CHANNEL = "mediumExplain/battery"
然后重写该函数并保持不变
override fun configureFlutterEngine( flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
      call, result ->
      // This method is invoked on the main thread.
      // TODO
    }
  }
然后在它添加这个方法,将得到电池百分比和重新发送到我们的通道
  private fun getBatteryLevel(): Int {
    val batteryLevel: Int
    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
      val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
      batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    } else {
      val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
      batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
    }
    return batteryLevel
  }
注意: 这些和 flutter 没有任何关系这是一个 android 原生 kotlin 代码,你一定不知道,你也可以从 android 或 ios 调用任何 api 即使你不知道原生代码,你可以在 stackoverflow 上搜索,找到任何你需要的相关内容。你只需要在这些代码之间建立一个通道
现在你的整个代码应该看起来像
package com.example.bateery_level
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import androidx.annotation.NonNull
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
class MainActivity: FlutterActivity() {
    private  val CHANNEL ="mediumExplain/battery"
    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
            // This method is invoked on the main thread.
                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 batteryLevel: Int
        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
            val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
            batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
        } else {
            val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
            batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
        }
        return batteryLevel
    }
}
现在回到你的 flutter 应用程序并运行它
下一步是 ios 配置,如果在 Windows 设备上运行,则跳过
IOS Configuration 配置
在 Xcode 中打开 IOS 文件夹
然后打开运行器目录中的 AppGenerate.swift
在 did FinishLaunchingWithOptions 方法中添加这两行
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    let batteryChannel = FlutterMethodChannel(name: "mediumExplain/battery",
                                              binaryMessenger: controller.binaryMessenger)
再加上这个
      batteryChannel.setMethodCallHandler({
        (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
          guard call.method == "getBatteryLevel" else {
            result(FlutterMethodNotImplemented)
            return
          }
          self.receiveBatteryLevel(result: result)
      })
然后在文件的底部添加这个函数
private func receiveBatteryLevel(result: FlutterResult) {
  let device = UIDevice.current
  device.isBatteryMonitoringEnabled = true
  if device.batteryState == UIDevice.BatteryState.unknown {
    result(FlutterError(code: "UNAVAILABLE",
                        message: "Battery level not available.",
                        details: nil))
  } else {
    result(Int(device.batteryLevel * 100))
  }
}
现在你的整个档案应该看看
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
      let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
       let batteryChannel = FlutterMethodChannel(name: "mediumExplain/battery",
                                                 binaryMessenger: controller.binaryMessenger)
      batteryChannel.setMethodCallHandler({
        (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
          guard call.method == "getBatteryLevel" else {
            result(FlutterMethodNotImplemented)
            return
          }
          self.receiveBatteryLevel(result: result)
      })
    GeneratedPluginRegistrant.register(with: self)
    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 level not available.",
                            details: nil))
      } else {
        result(Int(device.batteryLevel * 100))
      }
    }
}
then run the app from a rea ios device not on a simualtor

GitHub 仓库
Https://github.com/mohaberabi/flutter_method_channel
结束语
如果本文对你有帮助,请转发让更多的朋友阅读。
也许这个操作只要你 3 秒钟,对我来说是一个激励,感谢。
祝你有一个美好的一天~

© 猫哥
微信 ducafecat
https://wiki.ducafecat.tech
https://ducafecat.com