Flutter 跟 IOS 原生通信--UIKitView使用(二)

Flutter 跟 IOS 原生通信--数据传输(一)

以写一个iOS原生播放视频的view为例:

1、首先打开xcode的info.plist文件,添加io.flutter.embedded_views_preview设置为YES,如下图:


截屏2021-10-19 下午3.17.43.png

2、flutter添加 UiKitView,以下为futter部分全部代码:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class IosPlayer extends StatefulWidget {

  @override
  _IosPlayerState createState() => _IosPlayerState();
}

class _IosPlayerState extends State<IosPlayer> {
  @override
  Widget build(BuildContext context) {


    return Scaffold(
      appBar: AppBar(
        title: Center(
          child: Text('ios_player'),
        ),
      ),
      body: SafeArea(
        child: Container(
          width: double.infinity,
          height: double.infinity,
          color: Colors.white,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Container(
                width: double.infinity,
                height: 270,
                child: _playerView(context),
              )
            ],
          ),
        ),
      ),
    );
  }

  Widget _playerView(context){
     if (Platform.isIOS){
       return UiKitView(
         viewType: 'plugins/player',
         onPlatformViewCreated:_onPlatformViewCreated,
         creationParams: {
           'url':'https://www.apple.com/105/media/cn/researchkit/2016/a63aa7d4_e6fd_483f_a59d_d962016c8093/films/carekit/researchkit-carekit-cn-20160321_848x480.mp4'
         },
         creationParamsCodec: new JSONMessageCodec(),
       );
     }
     return SizedBox();
  }

  Future<void> _onPlatformViewCreated(int id) async {
    print("**********ok***********id="+id.toString());
  }
}

注意:creationParams字段是到iOS原生view的传值内容,是个map;
3、接下来在iOS原生部分,先创建要实现的view,代码如下:

.h

#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>
#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>
NS_ASSUME_NONNULL_BEGIN

@interface PlayerView : UIView
- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args registrar:(NSObject<FlutterPluginRegistrar>*)registrar;
@end

NS_ASSUME_NONNULL_END

.m

#import "PlayerView.h"

@interface PlayerView()<FlutterPlugin,AVPictureInPictureControllerDelegate>
@property (nonatomic,strong) FlutterMethodChannel *channel;
@property (nonatomic,strong) AVPictureInPictureController *pipVC;
@property (nonatomic,strong) AVPlayer *avPlayer;
@property (nonatomic,strong) AVPlayerLayer *playerLayer;
@end
@implementation PlayerView
{
    NSString *_nowUrl;
}

- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args registrar:(NSObject<FlutterPluginRegistrar>*)registrar
{
    self = [super init];
    if (self) {
        NSDictionary *dictionary = [NSDictionary dictionaryWithDictionary:args];
        _nowUrl = dictionary[@"url"];
        NSLog(@"==========dictionary:%@",dictionary);
        NSString *name = @"plugins";
        FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:name binaryMessenger:registrar.messenger];
        self.channel = channel;
        [registrar addMethodCallDelegate:self channel:channel];
        self.backgroundColor = [UIColor blackColor];
        [self initPlayer];
    }
    return self;
}

- (void)initPlayer{
    NSURL *url = [NSURL URLWithString:_nowUrl];
    @try {
        NSError *error = nil;
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionOrientationBack error:&error];
        [[AVAudioSession sharedInstance] setActive:YES error:&error];
    } @catch (NSException *exception) {
        NSLog(@"AvAudioSession发生错误");
    }
    self.avPlayer = [AVPlayer playerWithURL:url];
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
    self.pipVC = [[AVPictureInPictureController alloc] initWithPlayerLayer:self.playerLayer];
    [self.layer addSublayer:self.playerLayer];
    self.pipVC.delegate = self;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
}

- (void)layoutSubviews{
    [super layoutSubviews];
    NSLog(@"bounds:width:%f,height:%f",self.bounds.size.width,self.bounds.size.height);
    self.playerLayer.frame = self.bounds;
    [self play];
}

- (void)addListen{
    __weak typeof(self) weakSelf = self;
    [self.avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
        NSLog(@"当前时间:%f",CMTimeGetSeconds(weakSelf.avPlayer.currentItem.currentTime));
        NSLog(@"总时间%f",CMTimeGetSeconds(weakSelf.avPlayer.currentItem.duration));
    }];
}

- (void)play{
    [self.avPlayer play];
}

- (void)pause{
    [self.avPlayer pause];
}

- (void)seekTo:(int)time{
    CMTime nextTime = CMTimeMakeWithSeconds(time/1000, 1.0);
    [self.avPlayer seekToTime:nextTime];
}

- (void)replacePlay:(NSString *)urlString{
    NSURL *url = [[NSURL alloc]initWithString:urlString];
    AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url];
    [self.avPlayer replaceCurrentItemWithPlayerItem:item];
}

- (void)startPiP{
    if ([AVPictureInPictureController isPictureInPictureSupported]) {
        if (self.pipVC == nil){
            self.pipVC = [[AVPictureInPictureController alloc]initWithPlayerLayer:self.playerLayer];
            self.pipVC.delegate = self;
        }
        if (self.pipVC.pictureInPictureActive == NO){
            [self.pipVC startPictureInPicture];
        }
    }
}

- (void)stopPip{
    if ([AVPictureInPictureController isPictureInPictureSupported]) {
        if (self.pipVC == nil){
            self.pipVC = [[AVPictureInPictureController alloc]initWithPlayerLayer:self.playerLayer];
            self.pipVC.delegate = self;
        }
        if (self.pipVC.pictureInPictureActive){
            [self.pipVC stopPictureInPicture];
        }
    }
}


+ (void)registerWithRegistrar:(nonnull NSObject<FlutterPluginRegistrar> *)registrar {
    
}

- (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController{
    
}

- (void)pictureInPictureControllerDidStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController{
    
}

- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController failedToStartPictureInPictureWithError:(NSError *)error{
    
}

- (void)pictureInPictureControllerWillStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController{
    
}

- (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController{
    
}

- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL restored))completionHandler{
    
}

@end

4、创建FlutterPlatformView,代码如下:

.h

#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>

NS_ASSUME_NONNULL_BEGIN

@interface PlayerPlatformView : NSObject<FlutterPlatformView>
- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args registrar:(NSObject<FlutterPluginRegistrar>*)registrar;
@end

NS_ASSUME_NONNULL_END

.m

#import "PlayerPlatformView.h"
#import "PlayerView.h"

@interface PlayerPlatformView()
{
//这是要添加到flutter上的view,
    PlayerView *playerView;
}
@end
@implementation PlayerPlatformView

- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args registrar:(NSObject<FlutterPluginRegistrar>*)registrar
{
    self = [super init];
    if (self) {
        playerView = [[PlayerView alloc]initWithFrame:frame viewIdentifier:viewId arguments:args registrar:registrar];
    }
    return self;
}

- (UIView*)view{
    return  playerView;
}
@end

5、创建FlutterPlatformViewFactory,代码如下:

.h

#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN

@interface PlayerFactory : NSObject<FlutterPlatformViewFactory>
- (instancetype)initWith:(NSObject<FlutterPluginRegistrar>*)registrar;
@end

NS_ASSUME_NONNULL_END

.m

#import "PlayerFactory.h"
#import "PlayerPlatformView.h"
@interface PlayerFactory()
{
    id<FlutterPluginRegistrar> _registrar;
}

@end
@implementation PlayerFactory
- (instancetype)initWith:(NSObject<FlutterPluginRegistrar>*)registrar{
    self = [super init];
        if (self) {
            _registrar = registrar;
        }
        return self;
}

//实现协议方法,flutter根据widget自动计算出iOS的frame,
//自动生成的viewId和flutter那边对应,flutter传的参数args
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
                                   viewIdentifier:(int64_t)viewId
                                        arguments:(id _Nullable)args
{
    NSLog(@"====---f--==%@",NSStringFromCGRect(frame));
    NSLog(@"====-----==%lld",viewId);
    NSLog(@"=======%@",args);
    return [[PlayerPlatformView alloc] initWithFrame:frame viewIdentifier:viewId arguments:args registrar:_registrar];
}

//关于编码格式一定要和flutter那边统一,并且和传递的参数类型匹配,
//如果flutter传递的是json,map,编码必须是JSONMessageCodec,
//flutter传递的是string,编码必须是Stringcodec,否则会出错,加载不了view
- (NSObject<FlutterMessageCodec>*)createArgsCodec
{
    return  [[FlutterJSONMessageCodec alloc] init];
}

@end

6、创建FlutterPlugin,代码如下:

.h

#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN

@interface PlayerPlugin : NSObject<FlutterPlugin>
//+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar;
@end

NS_ASSUME_NONNULL_END

.m

#import "PlayerPlugin.h"
#import "PlayerFactory.h"
@implementation PlayerPlugin


+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar
{
    PlayerFactory *fc = [[PlayerFactory alloc] initWith:registrar];
    [registrar registerViewFactory:fc withId:@"plugins/player"];
}

@end

此处的plugins/player对应UiKitView的viewType

7、最后在didFinishLaunchingWithOptions方法里边初始化,代码如下:

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

推荐阅读更多精彩内容