Flutter学习-插件开发

Flutter学习-插件开发

前言

Flutter是移动开发的跨平台框架,在使用该框架开发过程中,很多原生如Android的系统版本、toast、定位、电量等功能,Flutter是无法实现的这些特定平台的功。这个场景下我们就只有使用插件来达到使用平台特定功能的效果。

那么插件是什么呢
Flutter中的插件是这样定义的:
一种专用的Dart包,其中包含用Dart代码编写的API,以及针对Android(使用Java或Kotlin)和/或针对iOS(使用ObjC或Swift)平台的特定实现。
从这个定义来看,一个插件应该包括平台代码、dart代码。但是这两部分代码功能怎么关联?
官网写到:
“如果你想开发一个调用特定平台API的包,你需要开发一个插件包,插件包是Dart包的专用版本。 插件包包含针对Android(Java或Kotlin代码)或iOS(Objective-C或Swift代码)编写的特定于平台的实现(可以同时包含Android和Ios原生的代码)。 API使用platform channels连接到特定平台(Android或IOS)。”
这里的“platform channels”即平台通道:https://flutterchina.club/platform-channels/
所以,写一个插件,我们要先了解平台通道是什么?

一、平台通道(platform channels)

平台通道是一个桥梁,应用的Flutter部分通过平台通道(platform channel)将消息发送到其应用程序的所在的宿主(iOS或Android)。
宿主监听的平台通道,并接收该消息。然后它会调用特定于该平台的API(使用原生编程语言) - 并将响应发送回客户端,即应用程序的Flutter部分。
官网图如下:
Flutter学习-插件开发_第1张图片
值得注意的是:
消息和响应是异步传递的,以确保用户界面保持响应(不会挂起)。

调用方式:

  1. 在客户端,MethodChannel (API)可以发送与方法调用相对应的消息。 在宿主平台上,MethodChannel 在Android((API) 和 FlutterMethodChannel iOS (API) 可以接收方法调用并返回结果。这些类允许您用很少的“脚手架”代码开发平台插件。
  2. 如果需要,方法调用也可以反向发送,宿主作为客户端调用Dart中实现的API。

支持数据类型:
官网说明支持的类型如下: (另外,这些值在消息中的序列化和反序列化会自动进行,使用者不必关心。PS:是否支持自定义类型的转换?还不清楚。估计是不支持)
Flutter学习-插件开发_第2张图片

二、创建插件

第一次接触Flutter,可能很疑惑,AS自带创建项目类型有好几个,有啥区别?

  1. Flutter Application: Flutter应用(可以独立运行到设备上,叫做应用)
  2. Flutter Plugin:Flutter插件(Plugin其实就是一个特殊的Package,包括了平台Native代码,无法独立运行。)
  3. Flutter Package:纯Dart组件 (避免代码重复,组件可供很多应用使用)
    现在开始来创建插件项目flutter_plugin_test:
    Flutter学习-插件开发_第3张图片

使用As自动创建插件项目flutter_plugin_test后,会自动生成平台代码,目录结构如下:
Flutter学习-插件开发_第4张图片
1.android目录:存放android代码
2.example目录:测试plugin的目录,可写代码测试plugin
3.ios目录:存放ios的平台代码
4.lib目录:存放dart代码
创建该插件项目时候,平台已经自动生成了平台版本号获取的代码。
其中:
MethodChannel类
构造函数如下:
const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]);
name:The logical channel on which communication happens, not null
( channel的名称,同平台代码中注册名字)
codec:The message codec used by this channel, not null.(消息编码,暂时不关心)。
Flutter与iOS和Android的交互就是通过这个MethodChannel。MethodChannel就是我们的信使,负责dart和原生代码通信。

如上面测试代码,channe name为flutter_plugin_test,此时对应到平台代码(android平台为例,在目录android下面):

package com.demo.flutterplugintest;

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;
import io.flutter.plugin.common.PluginRegistry.Registrar;

/** FlutterPluginTestPlugin */
public class FlutterPluginTestPlugin implements MethodCallHandler {
  /** Plugin registration. */
  public static void registerWith(Registrar registrar) {
    final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_plugin_test");
    channel.setMethodCallHandler(new FlutterPluginTestPlugin());
  }

  @Override
  public void onMethodCall(MethodCall call, Result result) {
    if (call.method.equals("getPlatformVersion")) {
      result.success("Android " + android.os.Build.VERSION.RELEASE);
    } else {
      result.notImplemented();
    }
  }
}

如上所述,平台代码中,需要注册这个channel。flutter通过一个这个channel name 才能才够在对应平台上找到对应的MethodChannel,从而实现flutter与平台的交互。
注册方式:
1.建立channel(对应到void registerWith(Registrar registrar)方法中进行)

final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_plugin_test");
channel.setMethodCallHandler(new FlutterPluginTestPlugin());

2.实现MethodCallHandler接口
因为channel注册时候setMethodCallHandler为FlutterPluginTestPlugin;故FlutterPluginTestPlugin需要实现该接口:

 public interface MethodCallHandler {
    void onMethodCall(MethodCall var1, MethodChannel.Result var2);
}

onMethodCall说明:
该接口onMethodCall再dart调用平台方法时,被执行。
MethodCall中保存了dart调用的方法名和参数。如下:

MethodCall :
public final class MethodCall {
public final String method; //调用方法名
public final Object arguments; //方法中的参数
....
}

Result中表示执行结果,最终会返回到dart层:

public interface Result {
    void success(@Nullable Object var1);

    void error(String var1, @Nullable String var2, @Nullable Object var3);

    void notImplemented();
}

其中,执行成功,此时通过success将结果Object返回到dart层。

channel方法调用
在lib目录的dart文件中,获取到flutter_plugin_test的MethodChannel,封装到FlutterPluginTest类中,通过invokeMethod去调用本地方法。因为是异步执行,故需要加上async,获取平台版本号。文件lib/flutter_plugin_test.dart

import 'dart:async';

import 'package:flutter/services.dart';

class FlutterPluginTest {
  static const MethodChannel _channel =
      const MethodChannel('flutter_plugin_test');

  static Future get platformVersion async {
    final String version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}

这样Flutter就可以访问,本地的方法了。
通常应用于访问本地资源,例如访问相机,本地存储,图片的选择等。

三、使用插件

由于插件flutter_plugin_test已经注册,并在dart中(lib/flutter_plugin_test.dart)提供了属性:platformVersion,所以使用插件很简单。
新建dart文件,import该插件后,调用方法属性就行了。
import ‘package:flutter_plugin_test/flutter_plugin_test.dart’;

try {
String platformVersion = await FlutterPluginTest.platformVersion;
} on PlatformException {
platformVersion = ‘Failed to get platform version.’;
}

四、总结

如上,Flutter创建插件就是这样一个过程。要理解MethodChannel进行本地和flutter代码的互相调用达到插件的效果。其实,Flutter还有其他的channel,如MessageChannel,EventChannel。后续再详细说明各个channel。

你可能感兴趣的:(Flutter框架)