编写 Flutter 插件步骤,及上传插件的那些坑。

2,220 阅读7分钟

关注公众号: 微信搜索 前端工具人 ; 收货更多的干货

原文链接: 本人博客园文章

一、开场白:

  1. 目前截止 flutter github start 数已经94.4K了,潜在的说明了这事以后App开发趋势。
  2. 作为新起之秀,后劲足但是社区生态不够完善。还有许多的功能还没有相应的插件。
  3. 很荣幸能为flutter社区做贡献, 插件地址 flutter_3des_plugin, github地址

二、步骤

2.1、 创建一个插件包

注:flutter 插件项目默认针对iOS代码使用Objective-CAndroid代码使用Java 。 除非你手动更改过(我的就是)

flutter create --org com.example --template=plugin hello

// 改变原生语言
flutter create --template=plugin -i swift -a kotlin hello
// 或者
flutter create --template=plugin -i objc -a java hello

2.2 二、 部分结构

  1. 插件包的Dart API
    • lib/hello.dart
  2. 插件包APIAndroid实现
    • lib/hello.dart
  3. 插件包APIios实现
    • ios/Classes/HelloPlugin.m
  4. 一个依赖于该插件的Flutter应用程序,来说明如何使用它
    • example/

2.3 实现package

编写业务代码 (注: 若修改了原生代码,则需要运行 flutter build apk, 你的example才能使用你编辑过的功能 )

使用Android Studio 或者 Vscode编辑Android代码, 编写代码之前,首先确保代码至少已经构建过一次(例如,cdhello/example; 在 flutter build apk

  1. 暴露出去的方法:目录为 根目录下的lib/hello.dart
class Flutter3desPlugin {
  static const MethodChannel _channel =
      const MethodChannel('hello');

  static Future<Stringget platformVersion async {
    final String version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
  // 加密方法  
  static Future<String> encrypt(String key, String data) async{
    return await _channel.invokeMethod('encrypt' , <String,dynamic>{'data':data,'key':key});
  }
} 
  1. Android 改动的目录为 android/src/main/java/com/yourcompany/​hello/HelloPlugin.java 里面的 onMethodCall 方法,如下,注释部分是需要添加的
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
// 把 if 优雅的改成 switch 并添加 encrypt 条件, 这将对应你所暴露出去的方法名
  switch (call.method) {
    case "getPlatformVersion":
      result.success("Android " + android.os.Build.VERSION.RELEASE);
      break;
    case "encrypt"// 方法映射
        String body = call.argument("data");
        String keys = call.argument("key");
        String key = keys + keys.substring(0,16);
        byte [] text = encrypt(hexStr2Bytes(key),hexStr2Bytes(body));
        result.success(bytes2HexStr(text));
      break;
    default:
      result.notImplemented();
      break;
}
}
private static final String algorithm = "DESede";

// 方法的实现
public static byte[] encrypt(byte[] key, byte[] body) {
  try {
      SecretKey deskey = new SecretKeySpec(key, algorithm);
      Cipher c1 = Cipher.getInstance(algorithm);
      c1.init(Cipher.ENCRYPT_MODE, deskey);
      return c1.doFinal(body);
  } catch (java.security.NoSuchAlgorithmException e1) {
      e1.printStackTrace();
  } catch (javax.crypto.NoSuchPaddingException e2) {
      e2.printStackTrace();
  } catch (java.lang.Exception e3) {
      e3.printStackTrace();
  }
  return null;
} 
........... (此处省略其他方法的实现)
  1. ios 改动目录为 ios\Classes\hello.m 里的 handleMethodCall 方法, 添加 encrypt 方法映射, 和 java 部分类似; 注释部分是需要添加的
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  FlutterMethodChannel* channel = [FlutterMethodChannel
      methodChannelWithName:@"flutter_3des_plugin"
            binaryMessenger:[registrar messenger]];
  Flutter3desPlugin* instance = [[Flutter3desPlugin 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 if([@"encrypt" isEqualToString:call.method]) { // 方法名映射, 注意和你所暴露出去的方法名一致
    NSDictionary* argsMap=call.arguments;
    NSString * data=argsMap[@"data"];
    NSString * key=argsMap[@"key"];
    NSString *batteryLevel = [self encrypt:data key:key];
    if (batteryLevel) {
        result(batteryLevel);
    } else
    {
        result([FlutterError errorWithCode:@"UNAVAILABLE"
        message:@"Battery info unavailable"
        details:nil]);
    }
  } else {
    result(FlutterMethodNotImplemented);
  }
}
// 业务代码的实现
- (NSString *)encrypt:(NSString *)data key:(NSString *)key {
    JKEncrypt * en = [[JKEncrypt alloc]init];
    //加密
    NSString * encryptStr = [en encrypt3DesData:data key:key];
    return encryptStr;
}
@end

2.4. 再然后就是运行测试你的代码

注: 不用你区分是android环境还是ios环境, 框架会自动区分, 你只需要编写原生代码就行, 目录 example\lib\main.dart

import 'package:flutter_3des_plugin/flutter_3des_plugin.dart';
@override
  void initState() {
    super.initState();
    encrypt();
  }
// 3des 加密 
 encrypt () {
    Flutter3desPlugin.encrypt(_key, _data).then((res) {
      // TODO: res就是加密后的数据
      setState(() {
        _result = res; 
      });
    });
  } 

四、发布package包,至 pub.dev

  1. 在发布之前,检查pubspec.yaml、README.md以及CHANGELOG.md文件,以确保其内容的完整性和正确性
  2. 并在根目录下的pubspec.yaml 文件顶部加上 version, author, homepage等字段, 执行下面命令后会有提示;成功的提示:执行完第二个命令并且授权谷歌登录后显示: Successfully uploaded package.
flutter packages pub publish --dry-run --server=https://pub.dartlang.org;
flutter packages pub publish; 或者   flutter packages pub publish;

五、 可能遇到的问题

若第四步行云流水没问题就不用往下看了。接下来说发布的坑(坑的我身心疲惫)现在总结下方法

5.1. 先来看错误有哪些:

// 错误1, 一直卡着, 绝望.....看到这些报错我都.....
Waiting for your authorization...
Authorization received, processing...
It looks like accounts.google.com is having some trouble.
Pub will wait for a while before trying to connect again.
OS Error: Operation timed out, errno = 60, address = accounts.google.com, port = 53481
pub finished with exit code 69
// 错误2, 一直卡着
Package has 2 warnings. Upload anyway (y/n)? y
Uploading...

5.2. 解决; 先不管报哪类错, 跟着下面步骤执行命令就对了

1、第一步

  • 用命令行(git,cmd)打开你的项目根目录下,运行下面命令,
  • 目的:解除国内镜像; 因为是发包到国外 (注: 以下命令只在当前终端端口有效, 你在别的窗口运行就都重置了)
unset FLUTTER_STORAGE_BASE_URL
unset PUB_HOSTED_URL

2、第二步 设置本地代理

// windows端口号默认是1080. 除非你改过, mac的话1081
set http_proxy=http://127.0.0.1:1080
set https_proxy=http://127.0.0.1:1080
// 上面的没代理成功就换成下面的
export http_proxy=http://127.0.0.1:1080
export https_proxy=http://127.0.0.1:1080

3、第三步 设置终端代理

  • 这时你需要一个国外服务器........ (这很尴尬, 便宜的100一年吧);
  • 然后下载小飞机(Shadowocks.exe)。添加你的服务器, 系统代理模式选为 全局模式**

4、第四步

  • 测试你的终端代理设置成功没
  • 上几部的命令行里输入 curl -vv http://www.google.com, 看是否能访问谷歌, 成功提示如下:

5、第五步

  • pinggoogle 后在执行发包命令, 你就会看到如下, 说明就成功了,
  • 这时去pub.dev 去搜索你的插件就能搜到了。
Package has 2 warnings. Upload anyway (y/n)? y
Uploading...
Successfully uploaded package.