1. 通过CocoaPods导入腾讯云超级播放器SDK
在项目/iOS/Podfile中添加 pod 'SuperPlayer'
执行pod install
2. 创建播放视图,遵循FlutterPlatformView
协议
PlatformView是 flutter 官方提供的一个可以嵌入 Android 和 iOS 平台原生 view 的小部件
//
// CZVideoPlayerView.swift
// cz_video_player
//
// Created by chaozheng on 2020/9/2.
//
import UIKit
import SuperPlayer
class CZVideoPlayerView: NSObject, FlutterPlatformView {
init(movieTitle: String, movieUrl: String) {
super.init()
// 监听电影信息
NotificationCenter.default.addObserver(self, selector: #selector(movieInfoNotification), name: NSNotification.Name.init(rawValue: movieInfoIdentifier), object: nil)
// 设置电影名称
superPlayerView.controlView.title = movieTitle
// 播放视频
superPlayerModel.videoURL = movieUrl
superPlayerView.play(with: superPlayerModel)
}
// MARK: - 收到电影信息
@objc func movieInfoNotification(notification: Notification) {
let movieIndoDict = notification.object as! Dictionary<String, Any>
// 电影名称
let movieTitle = movieIndoDict["movieTitle"] as? String ?? ""
// 电影地址
let movieUrl = movieIndoDict["movieUrl"] as? String ?? ""
// 设置电影名称
superPlayerView.controlView.title = movieTitle
// 播放视频
superPlayerModel.videoURL = movieUrl
superPlayerView.play(with: superPlayerModel)
}
private lazy var containerView: UIImageView = {
let view = UIImageView()
view.isUserInteractionEnabled = true
return view
}()
/// 腾讯视频播放器
private lazy var superPlayerView: SuperPlayerView = {
let view = SuperPlayerView(frame: containerView.bounds)
view.coverImageView.contentMode = .scaleAspectFill
view.autoPlay = true
view.fatherView = containerView
let superPlayerViewConfig = SuperPlayerViewConfig()
superPlayerViewConfig.maxCacheItem = 10000
view.playerConfig = superPlayerViewConfig
view.delegate = self
return view
}()
/// 腾讯视频播放器播放模型
public lazy var superPlayerModel: SuperPlayerModel = {
let model = SuperPlayerModel()
return model
}()
func view() -> UIView {
return containerView
}
deinit {
superPlayerView.resetPlayer()
NotificationCenter.default.removeObserver(self)
}
}
extension CZVideoPlayerView: SuperPlayerDelegate {
/// 返回事件
func superPlayerBackAction(_ player: SuperPlayerView!) {
NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: superPlayerBackActionIdentifier), object: nil)
}
/// 播放结束通知
func superPlayerDidEnd(_ player: SuperPlayerView!) {
NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: superPlayerDidEndIdentifier), object: nil)
}
/// 全屏改变通知
func superPlayerFullScreenChanged(_ player: SuperPlayerView!) {
}
}
3. 创建对应的视图工厂类,遵循FlutterPlatformViewFactory
协议
class CZVideoPlayerViewFactory: NSObject, FlutterPlatformViewFactory {
/**
* 返回platformview实现类
*@param frame 视图的大小
*@param viewId 视图的唯一表示id
*@param args 从flutter creationParams 传回的参数
*
*/
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
let argsDict = args as? Dictionary<String, String>
let movieTitle = argsDict?["movieTitle"] ?? ""
let movieUrl = argsDict?["movieUrl"] ?? ""
return CZVideoPlayerView(movieTitle: movieTitle, movieUrl: movieUrl)
}
//如果需要使用args传参到ios,需要实现这个方法,返回协议。否则会失败。
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
4. 注册工厂类、Flutter与iOS通信、iOS与Flutter通信
import UIKit
import Flutter
// 电影信息标识
let movieInfoIdentifier = "movieInfoIdentifier"
// 电影视图返回标识
let superPlayerBackActionIdentifier = "superPlayerBackActionIdentifier"
// 电影播放结束标识
let superPlayerDidEndIdentifier = "superPlayerDidEndIdentifier"
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
// 传递信息的回调
var eventSink: FlutterEventSink?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController;
// flutter 与 iOS 交互
let channel = FlutterMethodChannel(name: "cz_video_player/method_channel", binaryMessenger: controller as! FlutterBinaryMessenger)
channel.setMethodCallHandler { (call, result) in
if call.method == movieInfoIdentifier {
NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: movieInfoIdentifier), object: call.arguments)
}
}
// iOS主动 与 Flutter 交互
let eventChannel = FlutterEventChannel(name: "cz_video_player/event_channel", binaryMessenger: controller as! FlutterBinaryMessenger)
eventChannel.setStreamHandler(self)
// 监听播放视图上的返回按事件
NotificationCenter.default.addObserver(self, selector: #selector(superPlayerBackAction), name: NSNotification.Name.init(rawValue: superPlayerBackActionIdentifier), object: nil)
// 监听播放结束
NotificationCenter.default.addObserver(self, selector: #selector(superPlayerDidEnd), name: NSNotification.Name.init(rawValue: superPlayerDidEndIdentifier), object: nil)
// 注册视图
self.registrar(forPlugin: "CZVideoPlayerViewFactory")?.register(CZVideoPlayerViewFactory(), withId: "CZVideoPlayerViewFactory")
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
// 告知Flutter 点击了返回按钮
@objc func superPlayerBackAction(notification: Notification) {
if eventSink != nil {
eventSink!(
[
"type":"superPlayerBackAction",
"value": ""
]
)
}
}
// 告知Flutter 视频播放结束
@objc func superPlayerDidEnd(notification: Notification) {
if eventSink != nil {
eventSink!(
[
"type":"superPlayerDidEnd",
"value": ""
]
)
}
}
}
extension AppDelegate: FlutterStreamHandler {
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
eventSink = events
return nil
}
func onCancel(withArguments arguments: Any?) -> FlutterError? {
return nil
}
}
5. Flutter中加载视图、监听通知
class CZVideoPlayerWidget extends StatefulWidget {
CZVideoPlayerWidget(
{Key key,
@required this.movieTitle,
@required this.movieUrl,
this.coverImageUrl,
this.playBackBlock,
this.playDidEndBlock,
})
: super(key: key);
/// 电影名称
final String movieTitle;
/// 电影地址
final String movieUrl;
/// 封面图地址
final String coverImageUrl;
/// 返回按钮事件
final Function playBackBlock;
/// 播放结束事件
final Function playDidEndBlock;
@override
_CZVideoPlayerWidgetState createState() => _CZVideoPlayerWidgetState();
}
class _CZVideoPlayerWidgetState extends State<CZVideoPlayerWidget> {
// 注册一个通知, 传值 / 调用 / 获取 Flutter 主动向 iOS 调用
static const MethodChannel _methodChannel =
MethodChannel("cz_video_player/method_channel");
// 注册一个通知, 传值 / 调用 / 获取 iOS 主动向 Flutter 调用
static const eventChannel = EventChannel('cz_video_player/event_channel');
@override
void initState() {
// TODO: implement initState
super.initState();
// 监听 iOS 传递过来的值
eventChannel
.receiveBroadcastStream()
.listen(_eventChannelSucceed);
}
@override
Widget build(BuildContext context) {
// 传递视频相关信息
_methodChannel.invokeMethod("movieInfoIdentifier",
{"movieTitle": widget.movieTitle, "movieUrl": widget.movieUrl});
if (Platform.isAndroid) {
///加载安卓原生视图
return Text('这个平台老子不支持');
} else if (Platform.isIOS) {
return UiKitView(
creationParams: <String, String>{
"movieTitle": widget.movieTitle,
"movieUrl": widget.movieUrl,
},
creationParamsCodec: const StandardMessageCodec(),
viewType: "CZVideoPlayerViewFactory",
);
} else {
return Text('这个平台老子不支持');
}
}
// 播放视图上的返回按事件
void _eventChannelSucceed(dynamic data) {
String type = data["type"];
if (type == "superPlayerBackAction") { // 点击了返回按钮
widget.playBackBlock();
} else if (type == "superPlayerDidEnd") { // 视频播放结束
widget.playDidEndBlock();
}
}
}
测试
Container(
width: ScreenUtil.screenWidth,
height: ScreenUtil().setHeight(350),
child: CZVideoPlayerWidget(
movieTitle: "${widget.model.vodName} - ${movieSeriesTitles[_currentPlayIndex]}",
movieUrl: movieSeriesUrls[_currentPlayIndex],
coverImageUrl: widget.model.vodPic,
playBackBlock: () {
cz_print("playerBack", StackTrace.current);
},
),
)