在之前写过的《Flutter进阶—平台插件》中,简单介绍了如何创建和使用Flutter插件,现在可以尝试编写一个能够在平台与客户端之间传递数据的Flutter平台插件。首先,需要了解平台插件的基本原理。
平台通道的结构概述如下:使用 MethodChannel 在客户端(UI)和主机(平台)之间传递消息,消息和响应异步传递以确保用户界面保持响应。在客户端,Flutter 的 MethodChannel 类可以发送与方法调用相对应的消息;在平台端,Android 上的 MethodChannel 类和 iOS 上的 FlutterMethodChannel 类可以接收方法调用并发送结果。这些类允许开发者开发平台插件,并且方法调用也可以反向发送,让平台作为客户端调用Dart方法。
标准平台通道使用标准消息编解码器,支持将JSON格式的值二进制序列化,包括布尔值、数字、字符串、字节缓冲区以及列表和映射等。发送和接收值时,会自动进行序列化和反序列化。下面表格展示了Dart值与平台端类型的映射关系:
| Dart 类型 | Android 类型 | iOS 类型 |
|---|---|---|
| null | null | nil (NSNull when nested) |
| bool | java.lang.Boolean | NSNumber numberWithBool: |
| int | java.lang.Integer | NSNumber numberWithInt: |
| int (32位不足) | java.lang.Long | NSNumber numberWithLong: |
| int (64位不足) | java.math.BigInteger | FlutterStandardBigInteger |
| double | java.lang.Double | NSNumber numberWithDouble: |
| String | java.lang.String | NSString |
| Uint8List | byte[] | FlutterStandardTypedData typedDataWithBytes: |
| Int32List | int[] | FlutterStandardTypedData typedDataWithInt32: |
| Int64List | long[] | FlutterStandardTypedData typedDataWithInt64: |
| Float64List | double[] | FlutterStandardTypedData typedDataWithFloat64: |
| List | java.util.ArrayList | NSArray |
| Map | java.util.HashMap | NSDictionary |
了解原理后,可以通过IDE创建一个新的Flutter项目,例如名称为 share_to_wechat。默认项目模板使用Java编写Android代码、Objective-C编写iOS代码;如需使用Kotlin或Swift,可以使用 -i 与 -a 标志,例如执行 flutter create -i swift -a kotlin share_to_wechat 命令。
Flutter部分
首先创建Flutter平台客户端,构建通道。使用 MethodChannel 并确保通道名称唯一,建议使用前缀如 samples.flutter.test/plugin。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('samples.flutter.test/plugin');
String _returnData = '';
Future<Null> _dataInteraction() async {
String returnData;
try {
final int result = await platform.invokeMethod('dataInteraction');
returnData = '平台返回数据:$result';
} on PlatformException catch (e) {
returnData = '错误信息:${e.message}';
}
setState(() {
_returnData = returnData;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Text('${_returnData}'),
),
floatingActionButton: new FloatingActionButton(
onPressed: _dataInteraction,
tooltip: '获取平台数据',
child: new Icon(Icons.autorenew),
),
);
}
}
Android部分
在Android平台实现中,打开Flutter项目的Android部分,在 MainActivity.java 中创建 MethodChannel 并设置 MethodCallHandler,使用相同的通道名称。
package com.yourcompany.testplugin;
import android.os.Bundle;
import java.util.Random;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.test/plugin";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("dataInteraction")) {
int data = getData();
result.success(data);
} else {
result.notImplemented();
}
}
});
GeneratedPluginRegistrant.registerWith(this);
}
private int getData() {
Random ran = new Random();
return ran.nextInt(1000);
}
}
iOS部分
在iOS平台实现中,打开Flutter项目的iOS部分,在 AppDelegate.m 中创建 FlutterMethodChannel 并设置方法调用处理程序。
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
#import <Flutter/Flutter.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* batteryChannel = [FlutterMethodChannel
methodChannelWithName:@"samples.flutter.test/plugin"
binaryMessenger:controller];
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"dataInteraction" isEqualToString:call.method]) {
int data = [self getData];
result(@(data));
} else {
result(FlutterMethodNotImplemented);
}
}];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (int)getData {
int ran = arc4random() % 1000;
return ran;
}
@end
完成上述步骤后,即可在Android和iOS平台上运行应用程序,通过点击按钮获取平台返回的随机数据。这个例子展示了Flutter平台插件的基本实现,通过修改 getData 方法可以实现特定的平台功能,例如官方教程中获取电池电量的示例。平台插件的开发相对简单,关键在于理解通道机制和数据类型映射。