使用 Flutter 开发自定义插件

1,062 阅读4分钟

Flutter 是 Google 的免费开源 UI 应用程序开发工具包。它用于使用单个代码库在 Android 和 iOS 上构建高质量的本机界面。Flutter 的一件有趣的事情是它可以与现有代码一起工作,并且被全世界的开发人员和组织使用。在本文中,我们将学习如何使用 Flutter 开发自定义插件。

我们打算开发的 Flutter SDK 也可以在您的 Android 或 iOS 设备上运行。因此,我们想开发一个解决方案,我们可以在其中使用现有的 Android 和 iOS SDK 并开发 Flutter SDK。

所有的头脑风暴最终使我们产生了在 Flutter 中开发自定义插件的想法。自定义插件遵循基于 Flutter 灵活系统的基本机制,允许在 Android 上调用 Kotlin 或 Java 或 iOS 上的 Swift 或 Objective-C 代码中可用的平台特定 API。

跨不同渠道的 Flutter SDK 工作

Flutter 内置的特定于平台的 API 支持不依赖于代码生成,而是依赖于使用平台通道的灵活消息传递样式。要创建自定义插件,让我们详细了解 Flutter 架构:

  • 应用程序的 Flutter 部分  通过平台通道向其主机(应用程序的 iOS 或 Android 部分)发送消息。
  • 宿主 在平台通道 监听并接收消息。然后它调用任意数量的特定于平台的 API——使用本机编程语言——并将响应发送回客户端  即应用程序的 Flutter 部分,如下所示:

图片-3205a9439d543d60a426891702431e6baabbf11d-580x647-png

平台通道如何在不同平台之间工作的架构概述

使用 Flutter 构建自定义插件

入门

以下示例演示如何调用特定于平台的 API 来检索和显示当前电池电量。 它通过单一平台消息 使用 Android  BatteryManager API 和 iOS  API ,.device.batteryLevel``getBatteryLevel()

第 1 步:创建包

要创建插件包,

  • --template=plugin 将标志与 Flutter 命令一起 使用 create 。
  • 使用 --platforms= 后跟逗号分隔列表的选项来指定插件支持的平台。可用平台包括 Android、iOS、Web、Linux、macOS 和 Windows。
  • 使用该 --org 选项指定您的组织,使用反向域名表示法。该值用于生成的插件代码中的各种包和包标识符。
  • 使用 -a 选项指定 Android 的语言或 -i 选项指定 iOS 的语言。
  • 下面是为 Android、iOS 平台创建插件包的示例命令,同时为 Android 使用 java,为 iOS 使用 Objective-C。
flutter create
  • 此命令在文件夹中创建一个插件项目 batteryLevel ,其特定内容如下:

    • lib/batteryLevel.dart - 插件的 Dart API。
    • android/src/main/java/com/rudderstack/batteryLevel/BatteryLevelPlugin.java - Java 中插件 API 的特定于 Android 平台的实现。
    • ios/Classes/BatteryLevelPlugin.m - Objective-C 中插件 API 的 iOS 平台特定实现。
    • example/ - 依赖于插件并说明如何使用它的 Flutter 应用程序。

在 Flutter 网站上查看平台端如何接收不同的 dart 值,反之亦然。

第二步:创建 Flutter 平台客户端

应用程序的 State 类保存当前应用程序状态。扩展它以保持当前的电池状态。

  • MethodChannel 首先,使用返回电池电量的单一平台方法 构建通道 。
  • 通道的客户端和主机端通过在通道构造函数中传递的通道名称连接。

注意:单个应用中使用的所有频道名称必须是唯一的。

  • 使用唯一 域前缀为频道名称添加前缀。例如,  org.rudderstack.dev/battery
  • 打开 batteryLevel.dart 位于 lib 文件夹中的文件。
  • 创建 method 通道对象,如下所示,通道名称为 org.rudderstack.dev/battery
  • 请确保在 Android 和 iOS 平台上使用与 Flutter 中相同的名称初始化通道对象。
import 'dart:async';
import 'package:flutter/services.dart';
class BatteryLevel {  static const MethodChannel _channel =      MethodChannel('org.rudderstack.dev/battery');
 // Get battery level.}
  • 接下来,调用方法通道上的方法,使用字符串标识符指定要调用的具体方法 getBatteryLevel。例如,如果平台不支持平台 API(例如在模拟器中运行时),调用可能会失败。因此,将调用 invokeMethod 包装在一条 try-catch 语句中。
  • 获得电池电量后,使用以下代码将其返回:
// Get battery level.  static Future<String> getBatteryLevel() async {    String batteryLevel;    try {      final int result = await _channel.invokeMethod('getBatteryLevel');      batteryLevel = 'Battery level: $result%.';    } on PlatformException {      batteryLevel = 'Failed to get battery level.';    }    return batteryLevel;  }}
  • 现在,替换该 example/lib/main.dart 文件以包含一个小的用户界面,该界面以字符串形式显示电池状态和一个用于刷新值的按钮:
import 'package:flutter/material.dart';import 'dart:async';
import 'package:flutter/services.dart';import 'package:batteryLevel/batteryLevel.dart';
void main() {  runApp(MaterialApp(home: MyApp()));}
class MyApp extends StatefulWidget {  @override  _MyAppState createState() => _MyAppState();}
class _MyAppState extends State<MyApp> {  String _batteryLevel = 'Unknown';
  @override  void initState() {    super.initState();  }
  // Platform messages are asynchronous, so we initialize in an async method.  Future<void> _getBatteryLevel() async {    String batteryLevel;    // Platform messages may fail, so we use a try/catch PlatformException.    try {      batteryLevel = await BatteryLevel.getBatteryLevel();    } on PlatformException {      batteryLevel = 'Failed to get platform version.';    }
    // If the widget was removed from the tree while the asynchronous platform    // message was in flight, and we want to discard the reply rather than calling    // setState to update our non-existent appearance.    if (!mounted) return;
    setState(() {      _batteryLevel = batteryLevel;    });  }
  @override  Widget build(BuildContext context) {    return Material(      child: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.spaceEvenly,          children: [            ElevatedButton(              child: Text('Get Battery Level'),              onPressed: _getBatteryLevel,            ),            Text(_batteryLevel),          ],        ),      ),    );  }}

第 3 步:添加特定于 Android 平台的实现

打开 BatteryLevelPlugin.java 内部 android/src/main/java/com/rudderstack/batteryLevel/ 并进行如下更改:

  • MethodChannel 首先,将object  初始化中的channel名称改成 org.rudderstack.dev/battery 如下:
@Override  public void onAttachedToEngine(    @NonNull FlutterPluginBinding flutterPluginBinding  ) {    channel =      new MethodChannel(        flutterPluginBinding.getBinaryMessenger(),        "org.rudderstack.dev/battery"      );    channel.setMethodCallHandler(this);  }
  • 现在,替换 onMethodCall 为下面显示的定义来处理 getBatteryLevel 调用并响应 batteryLevel 如下:
@Override  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {    if (call.method.equals("getBatteryLevel")) {      result.success(99);    } else {      result.notImplemented();    }  }

第 4 步:添加特定于 iOS 平台的实现

打开 BatteryLevelPlugin.m 并 ios/Classes/ 进行以下更改:

  • FlutterMethodChannel 首先,将object  初始化中的channel名称改成 org.rudderstack.dev/battery 如下:
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {  FlutterMethodChannel* channel = [FlutterMethodChannel      methodChannelWithName:@"org.rudderstack.dev/battery"            binaryMessenger:[registrar messenger]];  BatteryLevelPlugin* instance = [[BatteryLevelPlugin alloc] init];  [registrar addMethodCallDelegate:instance channel:channel];}
  • handleMethodCall 接下来,用下面的定义  替换 方法来处理getBatteryLevel 调用并响应 batteryLevel 如下:
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {  if ([@"getBatteryLevel" isEqualToString:call.method]) {    result(@(99));  } else {    result(FlutterMethodNotImplemented);  }}

有了这个,我们已经成功开发了一个自定义插件。现在您可以在任意两个平台(Android 和 iOS)上运行该插件并了解它的工作原理。

发布自定义插件

让我们快速看一下开发自定义插件后需要牢记的一些说明:

  • 自定义插件开发完成后,您可以将自定义插件发布到 pub.dev  ,方便其他开发者使用。但是,在发布之前,请检查 pubspec.yaml、  README.md、 CHANGELOG.md和 LICENSE 文件以确保内容完整和正确。
  • 接下来在模式下运行publish命令, dry-run 看看是否都通过了分析:

$ flutter pub publish --dry-run

  • 下一步是发布到 pub.dev,但请确保您已准备就绪,因为发布是无法恢复的最后一步:

$ flutter pub 发布

有关发布的更多详细信息,请查看 dart.dev 上的发布文档 。