就像 React Native 同样,在 Flutter 应用中,若是须要调用第三方库的方法或者有一些功能须要使用原生的开发来提供,使用 Flutter Plugin 是一种不错的方式,它本质上就是一个 Dart Package,但与其它的 package 不一样点在于,Flutter 插件中通常都存在两个特殊的文件夹:android 与 ios,若是须要编写Java、Kotlin或者 Object-C 以及 Swift 代码,咱们就须要在这两个文件夹项目中进行,而后经过相应的方法将原生代码中开发的方法映射到 dart 中。html
本文以开发一个微信插件为例,为Flutter应用提供微信分享、登陆、支付等功能,项目代码能够直接在下方找到,也已经提交至Pub库:java
Pub库:pub.dartlang.org/packages/we… 项目地址:github.com/pantao/flut…android
要开发插件,可使用下面的代码快速基于 plugin 模板开始:ios
flutter create --template=plugin wechat
复制代码
上面的代码中,表示以plugin
模板建立一个名为wechat
的package
,建立完成以后,整个项目的目录结构就都提供好了,而且官方还提供了一些基本开发示例。git
- android // Android 相关原生代码目录
- ios // ios 相关原生代码目录
- lib // Dart 代码目录
- example // 一个完整的调用了咱们正在开发的插件的 Flutter App
- pubspec.yaml // 项目配置文件
复制代码
从 example/lib/main.dart 开始 在开发咱们的应用以后,先来了解一下 flutter 为咱们生成的文件们,打开 example/lib/main.dart,代码以下github
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:wechat/wechat.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
@override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
platformVersion = await Wechat.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('Running on: $_platformVersion\n'),
),
),
);
}
}
复制代码
这里须要特别注意的就是 initPlatformState() 方法中对 Wechat.platformVersion 的调用,这里面的 Wechat 就是咱们的插件,platformVersion 就是插件提供的 get 方法,跟着这个文件,找到 lib/wechat.dart 文件,代码以下:api
import 'dart:async';
import 'package:flutter/services.dart';
class Wechat {
static const MethodChannel _channel =
const MethodChannel('wechat');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
}
复制代码
在该文件中,能够看到 class Wechat 定义了一个 get 方法 platformVersion,它的函数体有点特别:bash
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
复制代码
咱们的 version 是经过 _channel.invokeMethod('getPlatformVersion') 方法的调用获得的,这个 _channel 就是咱们 Dart 代码与 原生代码进行通讯的桥了,也是 Flutter 原生插件的核心(固然,若是你编写的插件并不须要原生代码相关的功能,那么,_channel 就是无关紧要的了,好比咱们能够写一个下面这样的方法,返回 两个数字 a 与 b 的和:微信
class Wechat {
...
static int calculate (int a, int b) {
return a + b;
}
}
复制代码
以后,修改 example/lib/main.dart 代码:app
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
// 定义一个 int 型变量,用于保存计算结果
int _calculateResult;
@override
void initState() {
super.initState();
initPlatformState();
}
Future<void> initPlatformState() async {
String platformVersion;
try {
platformVersion = await Wechat.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
if (!mounted) return;
// init 的时候,计算一下 10 + 10 的结果
_calculateResult = Wechat.calculate(10, 10);
setState(() {
_platformVersion = platformVersion;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Container(
padding: EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Text('Running on: $_platformVersion\n'),
// 输出该结果
Text('Calculate Result: $_calculateResult\n'),
],
),
),
),
),
);
}
}
复制代码
不少时候,写插件,更多的是由于咱们须要让应用可以调用原生代码提供的方法,怎么作呢?
打开android/src/main/java/com/example/wechat/WechatPlugin.java 文件
,看以下代码:
package com.example.wechat;
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;
/** WechatPlugin */
public class WechatPlugin implements MethodCallHandler {
/** Plugin registration. */
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "wechat");
channel.setMethodCallHandler(new WechatPlugin());
}
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
}
}
复制代码
还记得上面提到的 getPlatformVersion 吗?还记得 _channel 那么,是否是在这里面也看到的对应的存在?没错, dart 中的 getPlatformVersion 经过 _channel.invokeMethod 发起一次请求,而后,Java 代码中的 onMethodCall 方法回被调用,该方法有两个参数:
MethodCall call:请求自己 Result result:结果处理方法 而后经过 call.method 能够知到 _channel.invokeMethod 中的方法名,而后经过 result.success 回调返回成功结果响应。
registerWith
在上面还有一小段代码 registerWith,能够看到里面有一个调用:
final MethodChannel channel = new MethodChannel(registrar.messenger(), "wechat");
channel.setMethodCallHandler(new WechatPlugin());
复制代码
这里就是在注册咱们的插件,将 wechat 注册成为咱们的 channel 名,这样,才不会调用 alipay 插件的调用最后到了 wechat 插件这里。
一样的,此次咱们打开 ios/Classes/WechatPlugin.m 文件:
#import "WechatPlugin.h"
@implementation WechatPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"wechat"
binaryMessenger:[registrar messenger]];
WechatPlugin* instance = [[WechatPlugin alloc] init];
[registrar addMethodCallDelegate:instance channel:channel];
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"getPlatformVersion" isEqualToString:call.method]) {
result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
} else {
result(FlutterMethodNotImplemented);
}
}
@end
复制代码
虽然语法有所不一样,可是,能够看到,跟 android 的 Java 代码结构上几乎是如出一辙的,首先 register 一个名为 wechat 的 channel,而后去 handleMethodCall,一样的经过 call.method拿到方法名,经过 result 作出响应。
接下来,咱们将前面的 caculate 方法,移到原生代码中来提供(虽然这很不必,但毕竟,只是为了演示嘛)。
在前面打开的 android/src/main/java/com/example/wechat/WechatPlugin.java 文件中,修改 onMethodCall 方法:
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else if (call.method.equals("calculate")) {
int a = call.argument("a");
int b = call.argument("b");
int r = a + b;
result.success("" + r);
} else {
result.notImplemented();
}
}
复制代码
添加了 call.method.equals("calculate") 判断,这里面具体的过程是:
调用 call.argument() 方法,能够取得由 wechat.dart 传递过来的参数 计算结果 调用 result.success() 响应结果 而后,咱们须要在 lib/wechat.dart 中修改 calculate 方法的实现,代码以下:
static Future<int> calculate (int a, int b) async {
final String result = await _channel.invokeMethod('calculate', {
'a': a,
'b': b
});
return int.parse(result);
}
复制代码
因为 _channel.invokeMethod 是一个异步操做,因此,咱们须要将 calculate 的返回类型修改成 Future,同时加上 async,此时咱们就能够直接使用 await 关键字了,跟 JavaScript 中的 await 同样,让咱们用同步的方式编写异步代码,在新版的 calculate 代码中,咱们并无直接计算 a+b 的结果,而是调用 _channel.invokeMethod 方法,将 a 与 b 传递给了 Java 端的 onMethodCall 方法,而后返回该方法返回的结果。 _channel.invokeMethod
该方法接受两个参数,第一个定义一个方法名,它是一个标识,简单来讲,它告诉原生端的代码,咱们此次是要干什么,第二个参数是一个 Map<String, dynamic> 型数据,是参数列表,咱们能够在原生代码中获取到。
接着,咱们须要更新一下对该方法的调用了,回到 example/lib/main.dart 中,修改为以下调用:
_calculateResult = await Wechat.calculate(10, 10);
复制代码
由于咱们如今的calculate
方法已是一个异步方法了。
若是咱们的插件须要支持 Android
与 IOS
两端,那么须要同步的在 ios
中实现上面的方法,打开 ios/Classes/WechatPlugin.m
文件,做以下修改:
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
NSDictionary *arguments = [call arguments];
if ([@"getPlatformVersion" isEqualToString:call.method]) {
result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
} else if ([@"calculate" isEqualToString:call.method]) {
NSInteger a = [arguments[@"a"] intValue];
NSInteger b = [arguments[@"b"] intValue];
result([NSString stringWithFormat:@"%d", a + b]);
} else {
result(FlutterMethodNotImplemented);
}
}
复制代码
实现过程与 java
端保持一致便可。
咱们的插件是能够提供微信的分享相关功能的,因此,确定须要用到第三方SDK,仍是从 Android 开始。
按 官方接入指南 所述,咱们须要添加依赖:
dependencies {
compile 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+'
}
复制代码
或
dependencies {
compile 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'
}
复制代码
前者带有统计功能,这很简单,打开 android/build.gradle 文件 ,在最下方粘贴以上片断便可:
...
android {
compileSdkVersion 27
defaultConfig {
minSdkVersion 16
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
}
dependencies {
compile 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+'
}
复制代码
而后,回到 WechatPlugin.java 文件,先添加一个 register 方法,它将咱们的Appid 注册给微信,仍是接着前面的 onMethodCall 中的 if 判断:
...
import com.tencent.mm.opensdk.openapi.WXAPIFactory;
...
else if (call.method.equals("register")) {
appid = call.argument("appid");
api = WXAPIFactory.createWXAPI(context, appid, true);
result.success(api.registerApp(appid));
}
...
复制代码
而后回到 lib/wechat.dart 添加相应调用:
...
/// Register app to Wechat with [appid]
static Future<dynamic> register(String appid) async {
var result = await _channel.invokeMethod(
'register',
{
'appid': appid
}
);
return result;
}
...
复制代码
此时,在咱们的 example
应该中,就能够调用 Wechat.register
方法,来注册应用了
按照官方 ios 接入指南所述,咱们能够经过 pod
添加依赖:
pod 'WechatOpenSDK'
复制代码
打开 ios/wechat.podspec ,能够看到以下内容:
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'wechat'
s.version = '0.0.1'
s.summary = 'A new flutter plugin project.'
s.description = <<-DESC
A new flutter plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
s.ios.deployment_target = '8.0'
end
复制代码
留意到数第三行的 s.dependency,这就是在指定咱们依赖 Flutter,若是有其它依赖在这里添加一行便可:
...
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
s.dependency 'WechatOpenSDK'
s.ios.deployment_target = '8.0'
end
复制代码
而后打开 ios/Classes/WechatPlugin.h 文件,修改以下:
#import <Flutter/Flutter.h>
#include "WXApi.h"
@interface WechatPlugin : NSObject<FlutterPlugin, WXApiDelegate>
@end
复制代码
再回到 ios/Classes/WechatPlugin.m,接着前面的 if 条件继续添加判断:
...
// Register app to Wechat with appid
else if ([@"register" isEqualToString:call.method]) {
[WXApi registerApp:arguments[@"appid"]];
result(nil);
}
...
复制代码
此时,咱们的插件已经支持微信 SDK 的 注册至微信 功能了,更多实现,本文就再也不讨论,有兴趣,能够直接下载完整项目,后面都是大同小异的实现,惟一须要的是,你须要有必定的 Java 编码与 Objective-C 编码能力。