You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter iOS端PlatformView嵌入AVPlayer黑屏无法显示视频如何解决

iOS端Flutter PlatformView嵌入AVPlayer黑屏问题修复

问题根因

当前代码黑屏是三个核心问题导致的:

  1. 初始化AVPlayerLayer时,PlatformView对应的_view还没完成UIKit布局,此时获取的bounds是CGRect.zero,给playerLayer设置的frame是0宽0高,没有可视区域。
  2. AVPlayer和AVPlayerLayer是preparePlayer()方法内的局部变量,方法执行完就会被ARC内存回收,播放器实例被销毁后自然无法渲染内容。
  3. 没有监听父视图的尺寸变化,后续视图布局更新时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 23:42:18