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

就像 React Native 一样,在 Flutter 应用中,如果需要调用第三方库的方法或者有一些功能需要使用原生的开发来提供,使用 Flutter Plugin 是一种不错的方式,它本质上就是一个 Dart Package,但与其它的 package 不同点在于,Flutter 插件中一般都存在两个特殊的文件夹:android 与 ios,如果需要编写Java、Kotlin或者 Object-C 以及 Swift 代码,我们就需要在这两个文件夹项目中进行,然后通过相应的方法将原生代码中开发的方法映射到 dart 中。

本文以开发一个微信插件为例,为Flutter应用提供微信分享、登录、支付等功能,项目代码可以直接在下方找到,也已经提交至Pub库:

Pub库:https://pub.dartlang.org/packages/wechat
项目地址:https://github.com/pantao/flutter-wechat

创建插件目录

要开发插件,可以使用下面的代码快速基于 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 protected]‘ }
  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 编码能力。

原文地址:https://blog.51cto.com/14332859/2419013

时间: 2024-11-05 10:46:27

Flutter 插件开发:以微信SDK为例的相关文章

PHP微信SDK——Zebra-Wechat

Zebra-Wechat 微信SDK 眼下处于开发状态,眼下实现了下面功能: 接收微信server推送信息,对推送信息类型进行识别 微信APIclient封装(用户管理.用户组管理.客服管理.自己定义菜单管理.系统管理等) 微信跳转验证封装 微信卡卷支持 博客地址:www.huyanping.cn 接收微信推送演示样例 use \Jenner\Zebra\Wechat\WechatServer; use \Jenner\Zebra\Wechat\Response\TextResponse; $t

群主微信sdk说明地址

群主微信sdk说明地址官网地址:http://weixin.senparc.com/ 源代码及最新更新:https://github.com/JeffreySu/WeiXinMPSDK 最新DLL发布地址:https://github.com/JeffreySu/Wei ... ixin.MP.BuildOutPut 所有源代码打包下载地址(永久地址,实时更新,给无法打开github网站的朋友使用.推荐直接同步github源代码,速度更快):https://github.com/JeffreyS

微信sdk (1)

<?php /** * wechat php test */ //define your token define("TOKEN", "weixin"); $wechatObj = new wechatCallbackapiTest(); $wechatObj->valid(); class wechatCallbackapiTest { public function valid() { $echoStr = $_GET["echostr&q

手把手教你简单接入微信SDK

就看微信现在这么火的样子,如果你的APP不接入微信的SDK好像就有点脱离了时代大车轮一样.一个成功的APP,不单单凭借着一个好的想法,一个好的功能,最主要还是用户量.用户量就好像是水,我们的APP就一艘船,而好的推广就像是好的帆.这艘船我可以造得很大,很华丽,但是少了水,我们仅仅是摆设:少了帆,我们仅仅靠桨来划,速度也太慢.因此接入微信SDK的功能也是必不可少,相信有点才能的领导都会要求加上这一个功能.好了,言归正传,正式开始手把手教你简单接入微信SDK. 1.首先我们需要建立一个android

基于.NET的轻量级微信SDK

一.前言 特别不喜欢麻烦的一个人,最近碰到了微信开发.下载下来了一些其他人写的微信开发“框架”,但是被恶心到了,实现的太臃肿啦. 最不喜欢的就是把微信返回的xml消息在组装成实体类,所以会比较臃肿,现在都提倡轻量级,所以有什么办法可以避免大量实体类的存在呢. 当然,还有包装的比较繁杂,看完官方API后,再看"框架",让人感觉一头雾水,不够清晰.明了. 二.我的实现思路 我的微信SDK(不敢自称框架),最重要的实现2个目标: 1.轻量级,就是要摒弃实体类,尽量少的申明Entity,减少S

超级懒汉编写的基于.NET的微信SDK

一.前言 特别不喜欢麻烦的一个人,最近碰到了微信开发.下载下来了一些其他人写的微信开发"框架",但是被恶心到了,实现的太臃肿啦. 最不喜欢的就是把微信返回的xml消息在组装成实体类,所以会比较臃肿,现在都提倡轻量级,所以有什么办法可以避免大量实体类的存在呢. 当然,还有包装的比较繁杂,看完官方API后,再看"框架",让人感觉一头雾水,不够清晰.明了. 二.我的实现思路 我的微信SDK(不敢自称框架),最重要的实现2个目标: 1.轻量级,就是要摒弃实体类,尽量少的申明

[转贴]超级懒汉编写的基于.NET的微信SDK

一.前言 特别不喜欢麻烦的一个人,最近碰到了微信开发.下载下来了一些其他人写的微信开发"框架",但是被恶心到了,实现的太臃肿啦. 最不喜欢的就是把微信返回的xml消息在组装成实体类,所以会比较臃肿,现在都提倡轻量级,所以有什么办法可以避免大量实体类的存在呢. 当然,还有包装的比较繁杂,看完官方API后,再看"框架",让人感觉一头雾水,不够清晰.明了. 二.我的实现思路 我的微信SDK(不敢自称框架),最重要的实现2个目标: 1.轻量级,就是要摒弃实体类,尽量少的申明

IOS9 微信sdk升级指南

升级IOS9之后,发现之前集成的微信SDK,无法使用isWXAppInstalled来判断是否安装微信.经过百度,发现是因为IOS9升级之后限制了http协议的访问. 微信SDK1.6.1官方的说明文档是这样的: 1.iOS 9系统策略更新,限制了http协议的访问,此外应用需要在"Info.plist"中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装.受此影响,当你的应用在iOS 9中需要使用微信SDK的相关能力(分享.收藏.支付.登录等)时,需要在&quo

微信登陆,微信SDK授权登陆经验分享

From:http://www.eoeandroid.com/thread-547012-1-1.html 最近因为项目需要做了微信登陆,好像也是微信最近才放出来的接口.还需要申请才能有权限实现授权.其实也比较简单,跟新浪微博和qq授权登陆差不多.不过还是有点差别,不知道是微信sdk本身就没有完善还是其他问题.会有一点蛋疼的地方,下面会细说.(由于等级不够,不能发在经验分享区,只能先写在这里了.希望对要做微信登陆的朋友有帮助,如果有不对的地方还希望大家直言不讳)<ignore_js_op> 首