likes
comments
collection
share

Flutter开发之Package和Plugin开发、测试、发布

作者站长头像
站长
· 阅读数 26

在flutter中有插件两个概念。包(Package)主要指对flutter相关功能的封装,类似于安卓中的插件和iOS中的三方库。而插件(Plugin)主要指通过插件调用原生的功能,如获取手机基本信息、获取原生的相机等。两者还是存在一定的差别的,Package一般只包含Dart代码,而插件除了包含有Dart外,还会包含有原生的语言,比如安卓中的Java或Kotlin,和iOS中的Objective-c或Swift。Package和Plugin都是为了封装一些基础组件、业务组件等,组件化开发,这样项目中多处用到的活多个项目用到的直接调用即可。

一、 包(Package)

创建Package主要有两种方式,分别为使用命令行创建和使用IDE创建。 命令行创建非常繁琐,需要指定类型、平台、支持的语言、路径等,不便于输入和修改,这里就不做具体的介绍,推荐使用IDE进行创建。

1.1 创建Package

本文使用Android Studio进行创建,首先需要选择该package使用的flutter sdk。

Flutter开发之Package和Plugin开发、测试、发布

然后需要输入插件名、文件目录、插件类型(可以选择是Package还是Plugin),支持开发平台及对应的开发语言。

Flutter开发之Package和Plugin开发、测试、发布

1.2 Package项目目录结构

Flutter开发之Package和Plugin开发、测试、发布

如图所示,Package项目的目录结构较为简单,主要是lib目录下的和项目同名类flutter_package_demo.dart,该类主要是用于在里面暴露出包中的类,以便其它项目调用。

Package中的代码实现放在lib目录下即可,测试代码写在test目录下的flutter_package_demo_test.dart中即可。

1.3 Package测试

这里是比较坑的一点,因为Package是个单纯的Dart库,没有安卓和iOS目录,所以不能直接运行该Package进行联调测试。本文提供一个可测试的方法,可在自己的项目中通过path引入该package,然后可以通过在项目中调用进行联调,有问题对package修改即可。

二、 插件(Plugin)

Plugin(插件)和Package最大的差别在于Plugin为支持跨平台的插件,可以通过插件调用原生安卓和iOS的功能,如获取手机版本信息、调用原生摄像头等,代码包含有Dart、安卓原生代码、iOS原生代码。

2.1 创建Plugin

这里和创建Package类似,不推荐使用终端进行创建,建议使用IDE进行创建,比较直观且易操作。

1、首先打开Android Studio,然后点击File->New->New Flutter Project。

2、进入到选择flutter sdk页面,选择自己本地的flutter,然后点击next。

3、设置plugin,如下图所示。

Flutter开发之Package和Plugin开发、测试、发布

4、项目目录

Flutter开发之Package和Plugin开发、测试、发布

如图所示,lib目录下的文件为具体实现,example是测试项目,可以对plugin进行测试,iOS目录下为iOS原生端实现,android目录下为安卓原生端实现。

【注】示例plugin是使用flutter3.3.5版本创建,不同版本目录结构可能会有细微差别。

2.2 插件plugin定义
  • 接口定义

这里先首先介绍下lib目录下的三个文件: flutter_plugin_demo_platform_interface.dart文件是定义接口的地方,在该文件只定义方法。如下代码所示,定义了一个新的printHelloWord方法。

/// 接口定义(主要定义方法,在channel中实现该方法,具体实现为原生实现对应的方法)
abstract class FlutterPluginDemoPlatform extends PlatformInterface {
  /// Constructs a FlutterPluginDemoPlatform.
  FlutterPluginDemoPlatform() : super(token: _token);

  static final Object _token = Object();

  static FlutterPluginDemoPlatform _instance = MethodChannelFlutterPluginDemo();

  /// The default instance of [FlutterPluginDemoPlatform] to use.
  ///
  /// Defaults to [MethodChannelFlutterPluginDemo].
  static FlutterPluginDemoPlatform get instance => _instance;

  /// Platform-specific implementations should set this with their own
  /// platform-specific class that extends [FlutterPluginDemoPlatform] when
  /// they register themselves.
  static set instance(FlutterPluginDemoPlatform instance) {
    PlatformInterface.verifyToken(instance, _token);
    _instance = instance;
  }

  /// 接口方法定义
  Future<String?> getPlatformVersion() {
    throw UnimplementedError('platformVersion() has not been implemented.');
  }

  Future<String?> printHelloWorld() {
    throw UnimplementedError('printHelloWorld() has not been implemented.');
  }
}
  • 接口实现

接口实现主要在channel文件中,该文件为interface的子类,负责具体接口的实现,但方法的具体实现依赖于调用原生端的实现。

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

import 'flutter_plugin_demo_platform_interface.dart';

/// An implementation of [FlutterPluginDemoPlatform] that uses method channels.
class MethodChannelFlutterPluginDemo extends FlutterPluginDemoPlatform {
  /// The method channel used to interact with the native platform.
  @visibleForTesting
  final methodChannel = const MethodChannel('flutter_plugin_demo');

  @override
  Future<String?> getPlatformVersion() async {
    final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
    return version;
  }

  @override
  Future<String?> printHelloWorld() async {
    // TODO: implement printHelloWord
    final str = await methodChannel.invokeMethod<String>('printHelloWorld');
    returm str;
  }
}

printHelloWorld()方法为例,在该文件中实现了该方法,在方法的实现为通过methodChannel调用的原生方法。

  • 调用

和plugin项目同名的类主要是供外部调用,若某一项目使用了该插件,则调用该插件的方法就是调用的该类中的方法。 这里的实现可以分为两类,一类为使用Dart直接实现,一类为需要依赖于原生端实现,需要调用interface接口类中的方法.

import 'flutter_plugin_demo_platform_interface.dart';

/// 供项目使用该plugin时调用
class FlutterPluginDemo {
  Future<String?> getPlatformVersion() {
    return FlutterPluginDemoPlatform.instance.getPlatformVersion();
  }

  void printHelloWorldDart() {
    // 直接Dart实现
    return print('Dart直接实现:print hello world');
  }

   Future<String?> printHelloWorld() {
    // 调用 FlutterPluginDemoPlatform 接口定义中方法,具体实现在channel子类中,channel中实现通过methodChannel调用原生方法
    return FlutterPluginDemoPlatform.instance.printHelloWorld();
  }
}
2.3 iOS原生实现

iOS目录主要为iOS原生端实现,这里需要注意一点的是最好是打开example下的iOS进行实现,这样一边实现一边可以进了调试。因项目默认没有pod文件,所以需要对example目录下的iOS进行pod install

Flutter开发之Package和Plugin开发、测试、发布

如图所示,具体实现是在测试项目中的pod中,该本地pod即为plugin中iOS,在这里可以进行iOS端的实现,本文已上述的printHelloWord为例。

实现: 这里需要注意的是因本文示例选择的是Swift语言,所以具体实现是在swift类中,若选择的是oc,则实现是在oc类中。其实swift的实现也是通过oc的类调用的swift。

FlutterPluginDemoPlugin类的.m类中可以看到调用的为SwiftFlutterPluginDemoPlugin,具体的实现是在该类中:

@implementation FlutterPluginDemoPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  [SwiftFlutterPluginDemoPlugin registerWithRegistrar:registrar];
}
@end

swift类中实现:

public class SwiftFlutterPluginDemoPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "flutter_plugin_demo", binaryMessenger: registrar.messenger())
    let instance = SwiftFlutterPluginDemoPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
      if call.method == "printHelloWorld" {
          // 实现
          result("这是iOS端实现,打印iOS:hello world")
      }
      
  }
}

2.4 测试

测试是使用Android Studio打开example测试工程进行测试。 测试代码在example->lib->main.dart中写,目录如图所示:

Flutter开发之Package和Plugin开发、测试、发布

然后在main.dart中可以通过插件调用获取到原生端的实现。

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';
  final _flutterPluginDemoPlugin = FlutterPluginDemo();

  String _helloWorldStr = "Hello World";

  @override
  void initState() {
    super.initState();
    _printHelloWorld();
  }

  // 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.
    // We also handle the message potentially returning null.
    try {
      platformVersion =
          await _flutterPluginDemoPlugin.getPlatformVersion() ?? 'Unknown platform version';
    } 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;
    });
  }

  Future<void> _printHelloWorld() async {
    String str;

    try {
      str =
          await _flutterPluginDemoPlugin.printHelloWorld() ?? '未获取到';
    } on PlatformException {
      str = 'Failed to get hello world.';
    }

    if (!mounted) return;

    setState(() {
      _helloWorldStr = str;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Text('Running on: $_helloWorldStr\n'),
        ),
      ),
    );
  }
}

测试模块代码如上所示,到这里就可以run测试工程查看结果了,运行结果如下图所示:

Flutter开发之Package和Plugin开发、测试、发布

三、发布

发布主要有两种,一种是发布到Flutter的官方,一种是发布到自己公司的私有库中,两者发布步骤大部分都是相同的,在一些地方存在差异,下面对这两种发布方法进行介绍。

3.1 检查四项文件

需要检查下Package或Plugin中是否包含有pubspec.yamlREADME.mdCHANGELOG.mdLICENSE这四个文件,需要检查下完整性。

Flutter开发之Package和Plugin开发、测试、发布

这四项内容默认创建的时候都会有,若缺少可以在项目终端中运行 flutter create .进行补全。

(1)pubspec.yaml

该文件主要是引用外部插件,其中namedescriptionversionhomepage需要填写完整,publish_to指将该库发布到指定位置,发布到私有库需要,若发布到pub.dev则不需要。

Flutter开发之Package和Plugin开发、测试、发布

(2)README.md

该文件主要是写插件或包的内容,便于别人查看时告知该库的作用。

Flutter开发之Package和Plugin开发、测试、发布

(3) CHANGELOG.md

该文件主要是记录插件或包的版本修改记录,说明每个版本的更新内容。

Flutter开发之Package和Plugin开发、测试、发布

(4) LICENSE

该文件主要是添加许可证书,若有的话可以写

Flutter开发之Package和Plugin开发、测试、发布

3.2 校验

校验较为简单,打开终端,进入插件或包的路径,然后运行

flutter packages pub publish --dry-run

Flutter开发之Package和Plugin开发、测试、发布

然后查看运行结果:

Flutter开发之Package和Plugin开发、测试、发布

警告信息为0,说明没什么问题,可以进行发布。

3.3 发布

接着校验的步骤,还是在终端当前插件或包下,运行命令进行发布,这里需要注意的是这时候最好先科学上网,然后再运行命令进行发布。

flutter packages pub publish 

发布到官方和私有库存在区别,若发布到官方,运行命令后会多一步登录谷歌账号校验的步骤,而发布到私有库不需要。

发布到私有库,需要进行私有仓库的搭建,这个网上有很多教程,可以参考下:Flutter私有仓库搭建

到这里对于Flutter中的Package和Plugin从创建、实现到测试,最后到发布全流程都介绍了,若有什么问题可以评论或留言。

转载自:https://juejin.cn/post/7230699871249924155
评论
请登录