为了账号安全,请及时绑定邮箱和手机立即绑定

Flutter 插件开发:以微信SDK为例

标签:
Java

创建插件目录

要开发插件,可以使用下面的代码快速基于 plugin 模板开始:

flutter create --template=plugin wechat

上面的代码中,表示以 plugin 模板创建一个名为 wechatpackage,创建完成之后,整个项目的目录结构就都提供好了,并且官方还提供了一些基本开发示例。

目录结构

- android // Android 相关原生代码目录 - ios // ios 相关原生代码目录 - lib // Dart 代码目录 - example // 一个完整的调用了我们正在开发的插件的 Flutter App - pubspec.yaml // 项目配置文件

从 example/lib/main.dart 开始
在开发我们的应用之后,先来了解一下 flutter 为我们生成的文件们,打开 example/lib/main.dart,代码如下

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 文件,代码如下:

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 的和:

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 系统

打开 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 系统

同样的,这次我们打开 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

在前面打开的 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 方法已经是一个异步方法了。

iOS

如果我们的插件需要支持 AndroidIOS 两端,那么需要同步的在 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

我们的插件是可以提供微信的分享相关功能的,所以,肯定需要用到第三方SDK,还是从 Android 开始。

Android 端 WechatSDK

官方接入指南 所述,我们需要添加依赖:

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

按照官方 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);   } ...



作者:Android技术开发
链接:https://www.jianshu.com/p/c182f5a1177f


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消