/flutter_pigeon_plugin

Primary LanguageObjective-COtherNOASSERTION

flutter_pigeon_plugin

一、使用场景

蓝牙、地图、扫码原生交互插件开发

二、方案

  1. 采用Pigeon注解生成模板代码

  2. 目的

  • 统一规范
  • 减少写重复代码

三、实现

1.命令创建Plugin

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

flutter_pigeon_plugin//插件名称

2.配置项目中pubspec.yaml添加配置

dev_dependencies: 

 flutter_test: 

   sdk: flutter 

 pigeon: 2.0.3

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

添加注解:@HostApi() Flutter调用原生的方法

@FlutterApi() 原生调用Flutter的方法

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);

}
  1. 执行命令,生成原生代码

首先创建存放生成文件的文件夹:

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"

  

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

  1. 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

//

//

#ifndef flutter_pigeon_plugin_h

#define flutter_pigeon_plugin_h

#import "AllTypesPigeon.h"

#endif /* flutter_pigeon_plugin_h */
  1. Android实现Flutter调用原生的方法

① 删除原有的获取版本号的channel的代码

FlutterPigeonPlugin.kt中继承AllTypesPigeon.HostEverything,并实现对应的方法

class FlutterPigeonPlugin extends 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方法,进行初始化和设置

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

  }

}

7.Flutter中实现插件调原生方法

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);
  }
}