Flutter进阶—简单平台插件实例

在之前写过的《Flutter进阶—平台插件》中,笔者简单介绍了如何简单的使用和创建一个Flutter插件,现在可以试试编写一个可以在平台与客户端之间传递数据的Flutter平台插件。在此之前,可以先了解一下平台插件的基本原理。

Flutter进阶—简单平台插件实例_第1张图片

上面图片是平台通道的结构概述,使用MethodChannel在客户端(UI)和主机(平台)之间传递消息,消息和响应异步传递,以确保用户界面保持响应。在客户端,Flutter的MethodChannel类可以发送与方法调用相对应的消息。在平台,Android上的MethodChannel类和iOS上的FlutterMethodChannel类可以接收方法调用并发送结果。这些类允许开发者开发一个平台插件,而且如果需要,方法调用也可以朝相反的方向发送,平台作为实现Dart方法的客户端。

标准平台通道使用的是标准消息编解码器,支持简单高效的将JSON格式的值二进制序列化,例如布尔值、数字、字符串、字节缓冲区以及这些数据的列表和映射,具体可以查看StandardMessageCodec类。发送和接收值时,会自动对这些值进行序列化和反序列化。下面的表格展示了在平台端如何接收Dart值,反之亦然。

Dart Android iOS
null null nil (NSNull when nested)
bool java.lang.Boolean NSNumber numberWithBool:
int java.lang.Integer NSNumber numberWithInt:
int, if 32 bits not enough java.lang.Long NSNumber numberWithLong:
int, if 64 bits not enough 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

了解完原理之后,可以通过idea编辑器创建一个新的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 {
  static const platform = const MethodChannel('samples.flutter.test/plugin');
  //...
}

接下来在MethodChannel上调用一个方法,指定通过String标识符dataInteraction调用的具体方法。如果当前平台不支持平台API,那么调用可能会失败,因此需要将invokeMethod调用包含在try-catch语句中。使用返回的结果来更新_returnData,以更新控件状态。

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;
    });
  }
  //...
}

最后将模板中的build方法替换为包含返回信息的文本控件,以及用于刷新返回信息的按钮。

class _MyHomePageState extends State<MyHomePage> {
  //...
  @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部分

在编写完Flutter部分的代码之后,使用Java编写Android平台特定的实现代码。为了插件的稳定性,在这里使用Java和Objective-C代码,如果喜欢也可以使用Kotlin和Swift的。首先在Android Studio中打开Flutter项目的Android主机部分,具体操作如下:

  1. 启动Android Studio
  2. 选择菜单项“File > Open…”
  3. 有导航到保存Flutter项目的目录,然后选择其中的android文件夹
  4. 打开位于Project视图中的java文件夹中的MainActivity.java文件

接下来,创建一个MethodChannel并在onCreate方法中设置一个MethodCallHandler,这里要确保使用与Flutter客户端上一样的通道名称。

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){
      }
    });

    GeneratedPluginRegistrant.registerWith(this);
  }
}

然后编写Java代码,用于调用Android上的随机函数,这跟在Android项目上编写的代码完全一样。在MainActivity类的onCreate方法下面中添加以下内容。

public class MainActivity extends FlutterActivity {
  //...
  @Override
  public void onMethodCall(MethodCall call, Result result){
  }

  private int getData() {
    Random ran = new Random();
    return ran.nextInt(1000);
  }
}

最后,在完成了之前添加的onMethodCall方法后,还需要处理一个平台方法dataInteraction,所以需要在call参数中测试它。这个平台方法的实现只是调用在上一步中编写的Android代码,并使用response参数传回成功和错误情况的响应。如果调用了一个未知的方法,会报告错误信息,修改下面的代码。

原代码:
public void onMethodCall(MethodCall call, Result result){
}

修改代码:
public void onMethodCall(MethodCall call, Result result){
  if(call.method.equals("dataInteraction")) {
      int data = getData();
      result.success(data);
  }else{
      result.notImplemented();
  }
}

现在就能够在Android上运行应用程序了,点击右下方的按钮就能获取Android主机返回的随机数字。

iOS部分

在确定Android主机与Flutter客户端沟通正常后,在Xcode中打开Flutter项目中的iOS主机部分:

  1. 启动Xcode
  2. 选择菜单项“File > Open…”
  3. 导航到保存Flutter项目的目录,然后选择其中的ios文件夹
  4. 确保Xcode项目没有错误地构建
  5. 在Project navigator中打开Runner > Runner下的AppDelegate.m文件

接下来,创建一个FlutterMethodChannel并在里面添加一个application didFinishLaunchingWithOptions:方法。这里需要确保与Flutter客户端上使用的是同一个通道名称。

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
#import 

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  // Override point for customization after application launch.
  FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
  FlutterMethodChannel* batteryChannel = [FlutterMethodChannel
                                          methodChannelWithName:@"samples.flutter.test/plugin"
                                          binaryMessenger:controller];
  [batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
  }];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

接下来,使用ObjectiveC代码添加获取随机数的功能,这段代码与在本机iOS应用程序中编写的代码完全相同。在AppDelegate类中添加以下作为新方法,添加在@end之前。

- (int)getData {
  int ran = arc4random() % 1000;
  return ran;
}

@end

最后,在完成了之前添加的setMethodCallHandler方法之后。还需要处理一个平台方法getData,所以要在call参数中测试。该平台方法的实现只需调用在上一步中编写的iOS代码,并使用result参数传回成功和错误情况的响应。如果调用了一个未知的方法,会报告错误信息。

[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
  if ([@"dataInteraction" isEqualToString:call.method]) {
    int data = [self getData];
    result (@(data));
  } else {
    result(FlutterMethodNotImplemented);
  }
}];

现在就能够在iOS上运行应用程序了,点击右下方的按钮就能获取iOS主机返回的随机数字。

Flutter进阶—简单平台插件实例_第2张图片

通过完成前面的例子可以看出,平台插件的实现其实很简单。只要把上面例子的getData方便修改成想要实现的平台特定功能,就能实现对应的平台插件,比如官方插件教程中就实现了通过特定平台API获取电池电量。

你可能感兴趣的:(dart,flutter,Flutter教程)