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

Flutter列表视图中启动新视频时停止其他视频播放方案咨询

解决ListView中多Better Player播放器互斥播放的问题

要实现“播放新视频时自动暂停当前视频”的效果,核心思路是统一管理所有视频控制器,并通过监听播放状态触发互斥逻辑:

1. 全局维护控制器集合

在ListView所在的父组件中,定义一个Map来存储每个视频对应的BetterPlayerController(用视频的唯一标识作为key,比如content的id):

Map<String, BetterPlayerController> _videoControllers = {};

2. 创建视频控制器并注册到集合

在VideoContent组件初始化时,创建对应的控制器并加入全局集合,同时监听播放状态,当视频开始播放时通知父组件:

// VideoContent组件内部
class VideoContent extends StatefulWidget {
  final String videoUrl;
  final String coverImage;
  final String? subtitle;
  final bool subtitlesEnabled;
  final Function(String videoId) onPlayStart; // 播放触发回调
  final String videoId; // 视频唯一标识

  const VideoContent(
    this.videoUrl,
    this.coverImage,
    this.subtitle,
    this.subtitlesEnabled, {
    required this.onPlayStart,
    required this.videoId,
    Key? key,
  }) : super(key: key);

  @override
  State<VideoContent> createState() => _VideoContentState();
}

class _VideoContentState extends State<VideoContent> {
  late BetterPlayerController _controller;

  @override
  void initState() {
    super.initState();
    // 初始化播放器控制器
    _controller = BetterPlayerController(
      BetterPlayerConfiguration(
        aspectRatio: 16/9,
        fit: BoxFit.contain,
      ),
      betterPlayerDataSource: BetterPlayerDataSource(
        BetterPlayerDataSourceType.network,
        widget.videoUrl,
        subtitles: widget.subtitle != null
            ? BetterPlayerSubtitlesSource.single(
                type: BetterPlayerSubtitlesSourceType.network,
                url: widget.subtitle!,
              )
            : null,
      ),
    );

    // 监听播放状态,开始播放时通知父组件
    _controller.addEventsListener((event) {
      if (event.betterPlayerEventType == BetterPlayerEventType.play) {
        widget.onPlayStart(widget.videoId);
      }
    });

    // 将控制器注册到全局集合
    WidgetsBinding.instance.addPostFrameCallback((_) {
      context.findAncestorStateOfType<_ParentWidgetState>()?._videoControllers[widget.videoId] = _controller;
    });
  }

  @override
  void dispose() {
    // 销毁时从集合移除并释放资源
    context.findAncestorStateOfType<_ParentWidgetState>()?._videoControllers.remove(widget.videoId);
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return BetterPlayer(controller: _controller);
  }
}

3. 父组件实现互斥暂停逻辑

在父组件中实现回调方法,当收到某视频开始播放的通知时,遍历所有控制器,暂停当前播放的其他视频:

// 父组件的回调方法
void _handleVideoPlay(String playingVideoId) {
  // 遍历所有控制器,暂停非当前播放的视频
  for (var entry in _videoControllers.entries) {
    final videoId = entry.key;
    final controller = entry.value;
    if (videoId != playingVideoId && controller.isPlaying()) {
      controller.pause();
    }
  }
}

// ListView的itemBuilder中修改VideoContent的调用
return SizedBox(
  height: MediaQuery.of(context).size.width * 9 / 16,
  child: VideoContent(
    content.value,
    content.image,
    content.videoSubtitle,
    subtitlesEnabled,
    videoId: content.id, // 传入视频唯一标识
    onPlayStart: _handleVideoPlay, // 传入回调
  ),
);

额外注意事项

  • 确保每个视频有唯一的videoId,避免控制器混淆;
  • 组件销毁时必须移除并释放控制器,防止内存泄漏;
  • 如果ListView存在item复用情况,需注意控制器的回收与重建逻辑,避免状态错乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 15:50:26