就像 React Native 同样,在 Flutter 应用中,若是须要调用第三方库的方法或者有一些功能须要使用原生的开发来提供,使用 Flutter Plugin 是一种不错的方式,它本质上就是一个 Dart Package,但与其它的 package 不一样点在于,Flutter 插件中通常都存在两个特殊的文件夹:android
与 ios
,若是须要编写Java、Kotlin或者 Object-C 以及 Swift 代码,咱们就须要在这两个文件夹项目中进行,而后经过相应的方法将原生代码中开发的方法映射到 dart 中。html
本文以开发一个微信插件为例,为Flutter应用提供微信分享、登陆、支付等功能,项目代码能够直接在下方找到,也已经提交至Pub库:java
Pub库:https://pub.dartlang.org/packages/wechat
项目地址:https://github.com/pantao/flutter-wechatandroid
要开发插件,可使用下面的代码快速基于 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
,它的函数体有点特别:微信
final String version = await _channel.invokeMethod('getPlatformVersion'); return version;
咱们的 version
是经过 _channel.invokeMethod('getPlatformVersion')
方法的调用获得的,这个 _channel
就是咱们 Dart 代码与 原生代码进行通讯的桥了,也是 Flutter 原生插件的核心(固然,若是你编写的插件并不须要原生代码相关的功能,那么,_channel
就是无关紧要的了,好比咱们能够写一个下面这样的方法,返回 两个数字 a
与 b
的和:app
class Wechat { ... static int calculate (int a, int b) { return a + b; } }
以后,修改 example/lib/main.dart
代码:异步
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());这里就是在注册咱们的插件,将
channel
名,这样,才不会调用alipay
插件的调用最后到了
一样的,此次咱们打开 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
编码能力。