Flutter 跟 IOS 原生通信--数据传输(一)
以写一个iOS原生播放视频的view为例:
1、首先打开xcode的info.plist文件,添加io.flutter.embedded_views_preview设置为YES,如下图:
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 {
@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 _onPlatformViewCreated(int id) async {
print("**********ok***********id="+id.toString());
}
}
注意:creationParams字段是到iOS原生view的传值内容,是个map;
3、接下来在iOS原生部分,先创建要实现的view,代码如下:
.h
#import
#import
#import
#import
NS_ASSUME_NONNULL_BEGIN
@interface PlayerView : UIView
- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args registrar:(NSObject*)registrar;
@end
NS_ASSUME_NONNULL_END
.m
#import "PlayerView.h"
@interface PlayerView()
@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*)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 *)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
#import
NS_ASSUME_NONNULL_BEGIN
@interface PlayerPlatformView : NSObject
- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args registrar:(NSObject*)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*)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
#import
NS_ASSUME_NONNULL_BEGIN
@interface PlayerFactory : NSObject
- (instancetype)initWith:(NSObject*)registrar;
@end
NS_ASSUME_NONNULL_END
.m
#import "PlayerFactory.h"
#import "PlayerPlatformView.h"
@interface PlayerFactory()
{
id _registrar;
}
@end
@implementation PlayerFactory
- (instancetype)initWith:(NSObject*)registrar{
self = [super init];
if (self) {
_registrar = registrar;
}
return self;
}
//实现协议方法,flutter根据widget自动计算出iOS的frame,
//自动生成的viewId和flutter那边对应,flutter传的参数args
- (NSObject*)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*)createArgsCodec
{
return [[FlutterJSONMessageCodec alloc] init];
}
@end
6、创建FlutterPlugin,代码如下:
.h
#import
#import
NS_ASSUME_NONNULL_BEGIN
@interface PlayerPlugin : NSObject
//+ (void)registerWithRegistrar:(NSObject*)registrar;
@end
NS_ASSUME_NONNULL_END
.m
#import "PlayerPlugin.h"
#import "PlayerFactory.h"
@implementation PlayerPlugin
+ (void)registerWithRegistrar:(NSObject*)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"]];