Flutter iOS端PlatformView嵌入AVPlayer黑屏无法显示视频如何解决
iOS端Flutter PlatformView嵌入AVPlayer黑屏问题修复
问题根因
当前代码黑屏是三个核心问题导致的:
- 初始化
AVPlayerLayer时,PlatformView对应的_view还没完成UIKit布局,此时获取的bounds是CGRect.zero,给playerLayer设置的frame是0宽0高,没有可视区域。 AVPlayer和AVPlayerLayer是preparePlayer()方法内的局部变量,方法执行完就会被ARC内存回收,播放器实例被销毁后自然无法渲染内容。- 没有监听父视图的尺寸变化,后续视图布局更新时playerLayer不会同步调整尺寸。
修复步骤
1. 重写原生端VideoView实现
把播放器、播放器图层改成类的强引用属性,同时添加视图布局监听,在父视图尺寸更新时同步调整播放器图层大小,修复后的代码如下:
import UIKit import Flutter import AVKit import AVFoundation public class VideoView: NSObject, FlutterPlatformView { // 强引用持有播放器和图层,避免被回收 private var _view: UIView private var player: AVPlayer? private var playerLayer: AVPlayerLayer? init( frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?, binaryMessenger messenger: FlutterBinaryMessenger? ) { _view = UIView() // 设置黑色背景匹配播放器场景,也可换成其他亮色调试PlatformView是否加载成功 _view.backgroundColor = .black super.init() createNativeView() } public func view() -> UIView { return _view } func createNativeView(){ preparePlayer() } private func preparePlayer() { guard let url = URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8") else { return } // 初始化播放器并赋值给类属性,强持有 player = AVPlayer(url: url) playerLayer = AVPlayerLayer(player: player) // 设置初始frame playerLayer?.frame = _view.bounds // 设置视频缩放模式 playerLayer?.videoGravity = .resizeAspect if let playerLayer = playerLayer { _view.layer.addSublayer(playerLayer) } // 监听视图布局变化,同步更新playerLayer尺寸 _view.addObserver(self, forKeyPath: "bounds", options: .new, context: nil) player?.play() } // KVO监听布局变化,更新播放器图层frame public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "bounds" { playerLayer?.frame = _view.bounds } } // 移除监听,避免内存泄漏 deinit { _view.removeObserver(self, forKeyPath: "bounds") player?.pause() player = nil } }
2. 确认PlatformView已正确注册
需要在iOS项目的AppDelegate.swift中注册你写的PlatformView工厂,确保viewType和Flutter端代码里的字符串完全一致,示例注册代码:
import Flutter import UIKit @main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // 注册PlatformView,viewType必须和Flutter端UiKitView传入的一致 let registrar = self.registrar(forPlugin: "VideoPlatformViewPlugin")! registrar.register( VideoViewFactory(messenger: registrar.messenger()), withId: "VideoPlatformView" ) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } // 实现PlatformView工厂 class VideoViewFactory: NSObject, FlutterPlatformViewFactory { private var messenger: FlutterBinaryMessenger init(messenger: FlutterBinaryMessenger) { self.messenger = messenger super.init() } func create( withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any? ) -> FlutterPlatformView { return VideoView( frame: frame, viewIdentifier: viewId, arguments: args, binaryMessenger: messenger ) } public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { return StandardMessageCodec() } }
3. 优化Flutter端代码
给UiKitView设置明确的尺寸约束,避免在Column、ListView等无界高度组件里布局异常,示例代码:
class VideoPlayerPlatformView extends StatelessWidget { static const StandardMessageCodec _decoder = StandardMessageCodec(); const VideoPlayerPlatformView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { if (defaultTargetPlatform == TargetPlatform.iOS) { // 给PlatformView设置明确宽高 return SizedBox( height: 220, width: double.infinity, child: const UiKitView( viewType: "VideoPlatformView", creationParamsCodec: _decoder, ), ); } return Container(); } }
额外排查点
如果按上述修改后还是无法正常播放,逐一检查以下配置:
- 确认测试设备/模拟器网络正常,可以正常访问示例m3u8视频地址
- 如果后续替换成HTTP协议的视频地址,需要在Info.plist中配置NSAppTransportSecurity放开HTTP访问限制
- 可以先把
_view的背景色改成红色,运行后如果能看到红色区域,说明PlatformView本身加载正常,问题出在播放器配置;如果连红色区域都看不到,说明PlatformView注册或者布局有问题。
内容的提问来源于stack exchange,提问作者userName
相关产品推荐
相关产品推荐

