Flutter Plugin 开发过程,详细记录

Demo地址

准备

1、nav端(ios、android)调试原生实现。
2、flutter_app(一个), 调试插件。
3、flutter_plugin(一个), 写插件。

三种Channel

BasicMessageChannel:用于传递字符串和半结构化的信息
MethodChannel:用于传递方法调用(method invocation)
EventChannel:用于数据流(event streams)的通信


BinaryMessage.png

Flutter端对三种Channel的书写

1、定义

//字符串需和原生保持一致
  MethodChannel _methodChannel = MethodChannel("flutter/live/methodChannel");
  EventChannel _eventChannel = EventChannel("flutter/live/eventChannel");
  BasicMessageChannel _messageChannel = BasicMessageChannel("flutter/live/messageChannel", StandardMessageCodec());

2、原生调用Flutter

_methodChannel.setMethodCallHandler((call) async {
      print('_methodChannel 收到:' + call.toString());
    });
_eventChannel.receiveBroadcastStream().listen((event) {
      print('_eventChannel 收到:' + event);
    });
_messageChannel.setMessageHandler((message) async {
      print('_messageChannel 收到:' + message);
    });

3、Flutter调用原生

_methodChannel.invokeMethod("startLive",{"url" : "rtmp://192.168.101.164"});

Map msg = {"startLive":"rtmp://192.168.101.164"};
                _messageChannel.send(msg);

原生端实现书写

两种方案
1、创建flutter plugin项目。

dio: ^3.0.6
  • 1.2 本地引用
flutter_ijkplayer:
    path:
      plugins/flutter_ijkplayer

2、直接在Flutter项目文件夹下的iOS文件夹和Android文件夹中写实现逻辑,所以这种方案只能在本项目使用。

iOS端对三种Channel的书写

1、创建
- (void)createMethodChannel:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterMethodChannel* methodChannel = [FlutterMethodChannel
    methodChannelWithName:@"flutter/live/methodChannel"
          binaryMessenger:[registrar messenger]];
    [registrar addMethodCallDelegate:self channel:methodChannel];
}
- (void)createEventChannel:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:@"flutter/live/eventChannel" binaryMessenger:[registrar messenger]];
    [eventChannel setStreamHandler:self];
}
- (void)createMessageChannel:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterBasicMessageChannel *messageChannel = [FlutterBasicMessageChannel messageChannelWithName:@"flutter/live/messageChannel" binaryMessenger:[registrar messenger]];
    [messageChannel setMessageHandler:^(id  _Nullable message, FlutterReply  _Nonnull callback) {
        NSLog(@"MessageChannel 收到:%@",message);
//        NSString *method=message[@"method"];
//        if ([method isEqualToString:@"startLive"]) {
//            NSLog(@"Flutter MessageChannel 收到:startLive");
//        }
    }];
}
2、监听回调

1、MethodChannel

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {

  if ([call.method isEqualToString:@"startLive"]) {
      NSLog(@"Flutter MethodChannel 收到:startLive");
  }
  else {
    result(FlutterMethodNotImplemented);
  }
}

2、EventChannel,通过绑定FlutterEventSink,让eventSink来发送消息

@property (nonatomic) FlutterEventSink eventSink;
- (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
    _eventSink = nil;
    return nil;
}

- (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
    _eventSink = events;
    return nil;
}

3、BasicMessageChannel 创建的时候绑定

注意事项:

如果直接在Flutter项目中直接写plugin,每次运行都会更新GeneratedPluginRegistrant这个类,是按podfile的描述重新生成,所以不要在GeneratedPluginRegistrant里边写。

1、可以直接在AppDelegate里写

2、可以封装FlutterChannelPlugin工具类,在AppDelegate入口函数调用

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  
  [GeneratedPluginRegistrant registerWithRegistry:self];
  
  [FlutterChannelPlugin registerWithRegistrar:[self registrarForPlugin:@"FlutterChannelPlugin"]];
  
}
  • 2.1、FlutterChannelPlugin需遵循代理(FlutterPlugin,FlutterStreamHandler)

  • 2.2、FlutterChannelPlugin需实现FlutterPlugin代理的类方法,作为入口

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterChannelPlugin *plugin = [[FlutterChannelPlugin alloc] init];
    [plugin createMethodChannel:registrar];
    [plugin createEventChannel:registrar];
    [plugin createMessageChannel:registrar];
}

3、如果需要跳转原生页面则需拿到当前控制器
可通过下面方法实现

@property(strong, nonatomic) UIViewController *viewController;

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  FlutterMethodChannel* channel = [FlutterMethodChannel
      methodChannelWithName:@"flutter_rtmp_plugin"
            binaryMessenger:[registrar messenger]];

  UIViewController *viewController =
    [UIApplication sharedApplication].delegate.window.rootViewController;

  FlutterRtmpPlugin* instance = [[FlutterRtmpPlugin alloc] initWithViewController:viewController];
  [registrar addMethodCallDelegate:instance channel:channel];
}

- (instancetype)initWithViewController:(UIViewController *)viewController {
  self = [super init];
  if (self) {
    self.viewController = viewController;
  }
  return self;
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  if ([call.method isEqualToString:@"startLive"]) {
      NSDictionary * dict = call.arguments;
      NSLog(@"流地址是 %@",dict[@"url"]);

      LFViewController *liveVC = [[LFViewController alloc] init];
      liveVC.liveUrl = dict[@"url"];
      liveVC.modalPresentationStyle = UIModalPresentationFullScreen;
      [self.viewController presentViewController:liveVC animated:YES completion:nil];
  }
  else {
    result(FlutterMethodNotImplemented);
  }
}

Android端的书写

MethodChannel, 另外两种Channel参照iOS,两者类似

1、入口函数中创建MethodChannel

 public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    final MethodChannel channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutterplugintemp");
    channel.setMethodCallHandler(this);
  }

2、遵循两个协议

implements FlutterPlugin, MethodCallHandler 

3、实现协议方法,在方法中写代码实现。

public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    if(call.method.equals("startLive")){
      Intent intent = new Intent(context,LivingActivity.class);
      String url = call.argument("url");
      intent.putExtra("url",url);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
      context.startActivity(intent);

    } else {
      result.notImplemented();
    }
注意事项

flutter 1.12版本之前的入口函数是

public static void registerWith(Registrar registrar) {

flutter 1.12版本以后是

public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {

若果写错会收不到Flutter的方法回调

2、若果要跳转原生页面则需要拿到app的当前context或activity
可通过这种写法获取

//
private Context context;

public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    FlutterRtmpPlugin plugin = new FlutterRtmpPlugin();
    plugin.context = flutterPluginBinding.getApplicationContext();
    final MethodChannel channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutter_rtmp_plugin");
    channel.setMethodCallHandler(plugin);
  }
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    if(call.method.equals("startLive")){
      Intent intent = new Intent(context,LivingActivity.class);
      String url = call.argument("url");
      intent.putExtra("url",url);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
      context.startActivity(intent);

    } else {
      result.notImplemented();
    }
  }

总结:

以上是plugin的基本使用,要想应付各种复杂的场景还需多参考google官方插件源码,来汲取养分。
比如 camera

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容