美文网首页Flutter utilsflutter软件
Flutter官方推荐插件开发辅助工具-Pigeon

Flutter官方推荐插件开发辅助工具-Pigeon

作者: xmb | 来源:发表于2021-09-13 18:33 被阅读0次

    工具插件:pigeon

    推荐必看的官方例子:pigeon_plugin_example

    其他pigeon写法的例子:
    pigeon/example
    pigeons

    Pigeon is a code generator tool to make communication between Flutter and the host platform type-safe, easier and faster.
    一个代码生成工具,让Flutter和宿主平台更安全、更简单、更快地通信。

    通过Dart入口,生成两端通用的模板代码,原生则只需重写模板内的接口,无需管理Method Channel的实现。参数可以通过模板来同步生成。

    目前的pigeon只支持生成OCJava代码。

    1、命令创建Plugin

    $ flutter create --template=plugin --platforms=android,ios -i swift -a kotlin flutter_pigeon_plugin

    2、flutter项目中pubspec.yamldev_dependencies中,添加pigeon插件依赖

    dev_dependencies:
      flutter_test:
        sdk: flutter
      flutter_lints: ^1.0.0
    
      pigeon: 
    

    3、在Flutter项目的lib目录外创建一个pigeons文件夹,在pigeons文件夹中创建all_types_pigeon.dart

    import 'package:pigeon/pigeon.dart';
    
    class Everything {
      bool? aBool;
      int? anInt;
      double? aDouble;
      String? aString;
      Uint8List? aByteArray;
      Int32List? a4ByteArray;
      Int64List? a8ByteArray;
      Float64List? aFloatArray;
      // ignore: always_specify_types
      List? aList;
      // ignore: always_specify_types
      Map? aMap;
      List<List<bool?>?>? nestedList;
      Map<String?, String?>? mapWithAnnotations;
    }
    
    /// Flutter调用原生的方法
    @HostApi()
    abstract class HostEverything {
      Everything giveMeEverything();
      Everything echo(Everything everything);
    }
    
    /// 原生调用Flutter的方法
    @FlutterApi()
    abstract class FlutterEverything {
      Everything giveMeEverythingFlutter();
      Everything echoFlutter(Everything everything);
    }
    
    

    4、执行命令

    首先创建存放生成文件的文件夹:
    android/src/mian下创建java/com/example/flutter_pigeon_plugin/文件夹,存放生成的Java文件。

    在项目目录~/flutter_pigeon_plugin下,执行以下命令:
    $ flutter pub run pigeon --input pigeons/all_types_pigeon.dart --dart_out lib/all_types_pigeon.dart --objc_header_out ios/Classes/AllTypesPigeon.h --objc_source_out ios/Classes/AllTypesPigeon.m --java_out android/src/main/java/com/example/flutter_pigeon_plugin/AllTypesPigeon.java --java_package "com.example.flutter_pigeon_plugin"

    命令拆解:
    ①` flutter pub run pigeon`  
    生成代码的命令
    
    ②` --input pigeons/all_types_pigeon.dart `
    指定生成代码的输入`dart`文件
    
    ③ `--dart_out lib/all_types_pigeon.dart `
    指定输出生成`dart`文件的目录文件
    
    ④ `--objc_header_out ios/Classes/AllTypesPigeon.h  `
    指定要生成的`iOS`的`.h`文件路径
    
    ⑤ `--objc_source_out ios/Classes/AllTypesPigeon.m  `
    指定要生成的`iOS`的`.m`文件路径
    
    ⑥ `--java_out android/src/main/java/com/example/flutter_pigeon_plugin/AllTypesPigeon.java `
    指定要生成的`Android`的`.java`文件路径
    
    ⑦  `--java_package "com.example.flutter_pigeon_plugin`
    指定`Android`的包名,在`android/src/main/`下的`AndroidManifest.xml`里的`package`
    
    ⑧ `--objc_prefix FLT`(可选)指定生成OC文件的前缀为FLT,前缀自己定义为自己的。
    

    可以参考官方的例子里的做法:
    ① 项目目录下创建一个run_pigeon.sh文件
    ② 每次有改动,执行命令:. ./run_pigeon.sh即可
    run_pigeon.sh文件内容如下:

    flutter pub run pigeon \
      --input pigeons/all_types_pigeon.dart \
      --dart_out lib/all_types_pigeon.dart \
      --objc_header_out ios/Classes/AllTypesPigeon.h \
      --objc_source_out ios/Classes/AllTypesPigeon.m \
      --objc_prefix FLT \
      --java_out android/src/main/java/com/example/flutter_pigeon_plugin/AllTypesPigeon.java \
      --java_package "com.example.flutter_pigeon_plugin"
    

    命令执行完成,会自动在命令中指定的几个位置生成响应的文件。

    5、iOS实现Flutter调用原生的方法

    ① 删掉项目中之前的获取版本的原生的和Flutter侧的相关channel代码
    ② 在AllTypesPigeon.m中自动生成了一个方法HostEverythingSetup

    void HostEverythingSetup(id<FlutterBinaryMessenger> binaryMessenger, NSObject<HostEverything> *api) {
      {
        FlutterBasicMessageChannel *channel =
          [FlutterBasicMessageChannel
            messageChannelWithName:@"dev.flutter.pigeon.HostEverything.giveMeEverything"
            binaryMessenger:binaryMessenger
            codec:HostEverythingGetCodec()];
        if (api) {
          NSCAssert([api respondsToSelector:@selector(giveMeEverythingWithError:)], @"HostEverything api doesn't respond to @selector(giveMeEverythingWithError:)");
          [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
            FlutterError *error;
            Everything *output = [api giveMeEverythingWithError:&error];
            callback(wrapResult(output, error));
          }];
        }
        else {
          [channel setMessageHandler:nil];
        }
      }
      {
        FlutterBasicMessageChannel *channel =
          [FlutterBasicMessageChannel
            messageChannelWithName:@"dev.flutter.pigeon.HostEverything.echo"
            binaryMessenger:binaryMessenger
            codec:HostEverythingGetCodec()];
        if (api) {
          NSCAssert([api respondsToSelector:@selector(echoEverything:error:)], @"HostEverything api doesn't respond to @selector(echoEverything:error:)");
          [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
            NSArray *args = message;
            Everything *arg_everything = args[0];
            FlutterError *error;
            Everything *output = [api echoEverything:arg_everything error:&error];
            callback(wrapResult(output, error));
          }];
        }
        else {
          [channel setMessageHandler:nil];
        }
      }
    }
    

    ③ 在SwiftFlutterPigeonPlugin.swift的注册方法里,调用这个setup方法进行初始化和设置

        public static func register(with registrar: FlutterPluginRegistrar) {
            let messenger: FlutterBinaryMessenger = registrar.messenger()
            let api: HostEverything & NSObjectProtocol = SwiftFlutterPigeonPlugin.init()
            HostEverythingSetup(messenger, api)
        }
    

    SwiftFlutterPigeonPlugin遵循HostEverything协议,实现Flutter调原生的方法

    import Flutter
    import UIKit
    
    /// 遵循HostEverything协议,实现Flutter调原生的方法
    public class SwiftFlutterPigeonPlugin: NSObject, FlutterPlugin, HostEverything {
        public static func register(with registrar: FlutterPluginRegistrar) {
            let messenger: FlutterBinaryMessenger = registrar.messenger()
            let api: HostEverything & NSObjectProtocol = SwiftFlutterPigeonPlugin.init()
            HostEverythingSetup(messenger, api)
        }
    
        // MARK: HostEverything
    
        public func giveMeEverythingWithError(_ error: AutoreleasingUnsafeMutablePointer<FlutterError?>) -> Everything? {
            let everyThing = Everything()
            everyThing.aString = "原生返给Flutter的字符串"
            everyThing.aBool = false
            everyThing.anInt = 11
            return everyThing
        }
    
        /// 遵循HostEverything协议,实现Flutter调原生的方法
        public func echo(_ everything: Everything, error: AutoreleasingUnsafeMutablePointer<FlutterError?>) -> Everything? {
            everything.aString = "原生交换的给Flutter的字符串"
            everything.aBool = false
            everything.anInt = 12
            return everything
        }
    }
    
    

    iOS/Classes目录下,创建flutter_pigeon_plugin.h文件,导入头文件,此文件在iOS自动生成的<flutter_pigeon_plugin/flutter_pigeon_plugin-Swift.h>文件中会自动引用。

    //
    //  flutter_pigeon_plugin.h
    //  Pods
    //
    //  Created by yuanzhiying on 2021/9/13.
    //
    
    #ifndef flutter_pigeon_plugin_h
    #define flutter_pigeon_plugin_h
    
    #import "AllTypesPigeon.h"
    
    #endif /* flutter_pigeon_plugin_h */
    
    

    6、 flutter_pigeon_plugin.dart中实现插件调原生方法

    import 'dart:async';
    
    import 'package:flutter_pigeon_plugin/all_types_pigeon.dart';
    
    class FlutterPigeonPlugin {
      static final HostEverything _hostEverything = HostEverything();
    
      /// Flutter 调用原生方法
      static Future<Everything> getEverything() async {
        return await _hostEverything.giveMeEverything();
      }
    
      /// Flutter 调用原生方法
      static Future<Everything> echoEveryThing(Everything everything) async {
        return await _hostEverything.echo(everything);
      }
    }
    
    

    7、使用插件的方法

      Future<void> getHostData() async {
        /// 通过插件调用原生方法
        Everything everything = await FlutterPigeonPlugin.getEverything();
        debugPrint('everything.aString: ${everything.aString}');
        debugPrint('everything.aBool: ${everything.aBool}');
        debugPrint('everything.anInt:${everything.anInt}');
      }
    
      void echoHostData() async {
        Everything echoEveryThing = Everything();
        echoEveryThing.aString = '我要跟原生交换的字符串';
        echoEveryThing.aBool = true;
        echoEveryThing.anInt = 10;
    
        /// 通过插件调用原生方法
        Everything everything = await FlutterPigeonPlugin.echoEveryThing(echoEveryThing);
        debugPrint('everything.aString: ${everything.aString}');
        debugPrint('everything.aBool: ${everything.aBool}');
        debugPrint('everything.anInt:${everything.anInt}');
      }
    

    至此,flutter调用原生iOS方法完成。

    8、flutter调用安卓原生的实现

    ① 删除原有的获取版本号的channel的代码
    FlutterPigeonPlugin.kt中继承AllTypesPigeon.HostEverything,并实现对应的方法

      override fun giveMeEverything(): AllTypesPigeon.Everything {
        var everything: AllTypesPigeon.Everything = AllTypesPigeon.Everything()
        everything.aString = "原生返给Flutter的字符串"
        everything.aBool = false
        everything.anInt = 11
        return everything
      }
    
      override fun echo(everything: AllTypesPigeon.Everything?): AllTypesPigeon.Everything? {
        everything?.aString = "原生交换的给Flutter的字符串"
        everything?.aBool = false
        everything?.anInt = 12
        return everything
      }
    

    ③ 通过自动生成的setup方法,进行初始化和设置

      override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        AllTypesPigeon.HostEverything.setup(flutterPluginBinding.binaryMessenger, this)
      }
    
      override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
        AllTypesPigeon.HostEverything.setup(binding.binaryMessenger, null)
      }
    

    最终如下:

    package com.example.flutter_pigeon_plugin
    
    import androidx.annotation.NonNull
    
    import io.flutter.embedding.engine.plugins.FlutterPlugin
    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
    
    /** FlutterPigeonPlugin */
    class FlutterPigeonPlugin: FlutterPlugin, MethodCallHandler, AllTypesPigeon.HostEverything {
    
      override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        AllTypesPigeon.HostEverything.setup(flutterPluginBinding.binaryMessenger, this)
      }
    
      override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
        result.notImplemented()
      }
    
      override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
        AllTypesPigeon.HostEverything.setup(binding.binaryMessenger, null)
      }
    
      override fun giveMeEverything(): AllTypesPigeon.Everything {
        var everything: AllTypesPigeon.Everything = AllTypesPigeon.Everything()
        everything.aString = "原生返给Flutter的字符串"
        everything.aBool = false
        everything.anInt = 11
        return everything
      }
    
      override fun echo(everything: AllTypesPigeon.Everything?): AllTypesPigeon.Everything? {
        everything?.aString = "原生交换的给Flutter的字符串"
        everything?.aBool = false
        everything?.anInt = 12
        return everything
      }
    }
    
    

    项目代码见:flutter_pigeon_plugin

    相关文章

      网友评论

        本文标题:Flutter官方推荐插件开发辅助工具-Pigeon

        本文链接:https://www.haomeiwen.com/subject/sczgwltx.html