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

Flutter中PlatformView视频播放器尺寸变更后停止播放求助

Flutter PlatformView 尺寸变更时维持视频播放状态问题

视频播放器尺寸变更演示

复现代码

import 'package:flutter/material.dart';
import 'package:native_video_player/native_video_player.dart';
import 'package:video_player/video_player.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late VideoPlayerController _controller;
  late NativeVideoPlayerController controllerPV;
  double sizedHeight=250.0;

  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.networkUrl(
      Uri.parse('https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/WhatCarCanYouGetForAGrand.mp4'),
      videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
    );
    _controller.addListener(() {
      setState(() {});
    });
    _controller.setLooping(true);
    _controller.initialize();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home:Scaffold(
        appBar: AppBar(title: const Text('Video Player')),
        body: Column(
          children: [
            SizedBox(
              height: sizedHeight,
              child: AspectRatio(
                aspectRatio: 16/9,
                child: VideoPlayer(_controller),
              ),
            ),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  if (_controller.value.isPlaying) {
                    _controller.pause();
                    controllerPV.pause();
                  } else {
                    _controller.play();
                    controllerPV.play();
                  }
                });
              },
              child: Icon(_controller.value.isPlaying ? Icons.pause : Icons.play_arrow),
            ),
            SizedBox(
              height: sizedHeight,
              child: AspectRatio(
                aspectRatio: 16/9,
                child: NativeVideoPlayerView(
                  onViewReady: (controller) async {
                    controllerPV=controller;
                    final videoSource = await VideoSource.init(
                      path: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/WhatCarCanYouGetForAGrand.mp4',
                      type: VideoSourceType.network,
                    );
                    await controller.loadVideoSource(videoSource);
                  },
                ),
              ),
            ),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  sizedHeight=220.0;
                });
              },
              child: const Icon(Icons.lock_reset_outlined),
            ),
          ],
        ),
      )
    );
  }
}

问题描述

我在代码中同时使用两个播放器播放同一视频:

  • 第一个基于video_player: ^2.8.2,尺寸变更时视频能正常播放无中断;
  • 第二个基于native_video_player: ^1.3.1,通过PlatformView实现(Android端采用ExoPlayer),尺寸变更时视频会停止播放。

其他采用PlatformView的Flutter视频播放器也存在该问题,我自己开发的基于Android视频编辑器API的视频编辑器,用PlatformView展示视频视图时也碰到了同样情况。代码中通过按钮修改播放器尺寸来模拟场景,希望找到在Flutter的PlatformView中实现尺寸变更时维持视频播放器状态的方案。


解决思路与方案

1. 防止PlatformView不必要重建

使用AutomaticKeepAliveClientMixin保持Widget状态,避免尺寸变化触发的Widget重建导致PlatformView销毁:

class _MyAppState extends State<MyApp> with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context); // 必须调用
    // 原build代码保持不变
  }
}

2. 自定义保持状态的PlatformView容器

将NativeVideoPlayerView封装到保持状态的Widget中,确保尺寸变化时仅更新容器尺寸,不重建PlatformView:

class KeepAliveVideoPlayer extends StatefulWidget {
  final double height;
  final void Function(NativeVideoPlayerController) onViewReady;

  const KeepAliveVideoPlayer({
    Key? key,
    required this.height,
    required this.onViewReady,
  }) : super(key: key);

  @override
  State<KeepAliveVideoPlayer> createState() => _KeepAliveVideoPlayerState();
}

class _KeepAliveVideoPlayerState extends State<KeepAliveVideoPlayer> with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return SizedBox(
      height: widget.height,
      child: AspectRatio(
        aspectRatio: 16/9,
        child: NativeVideoPlayerView(
          onViewReady: widget.onViewReady,
        ),
      ),
    );
  }
}

替换原代码中对应的NativeVideoPlayer部分为这个容器即可。

3. 原生层优化(针对自定义插件)

如果使用的是自己开发的插件,在Android端修改PlatformView实现:

  • 监听视图尺寸变化回调,直接更新ExoPlayer的Surface尺寸;
  • 确保ExoPlayer实例在PlatformView生命周期内唯一,仅在视图销毁时释放资源。

4. 兜底方案:记录并恢复播放状态

如果无法避免重建,可在尺寸变化前记录播放状态,重建后恢复:

ElevatedButton(
  onPressed: () {
    setState(() {
      // 记录当前状态
      final currentPosition = controllerPV.position;
      final wasPlaying = controllerPV.isPlaying;
      sizedHeight=220.0;
      // 等待重建完成后恢复
      WidgetsBinding.instance.addPostFrameCallback((_) async {
        if (wasPlaying) {
          await controllerPV.seekTo(currentPosition ?? Duration.zero);
          controllerPV.play();
        }
      });
    });
  },
  child: const Icon(Icons.lock_reset_outlined),
),

内容的提问来源于stack exchange,提问作者Lins Louis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:20:56