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

如何结合Riverpod与video_player实现播放暂停按钮图标联动更新?

问题:Flutter视频播放按钮图标无法随状态更新(Riverpod)

我正在通过书籍学习Flutter,这是我首次提问,若表述不清请见谅。我希望实现视频播放时按钮显示暂停图标,暂停时显示播放图标。目前我可以通过Riverpod Provider控制视频播放/暂停,但按钮图标无法更新。请问这种情况是否应该使用StreamProvider?

我的Provider代码:

final VideoPlayerController vpc =
    VideoPlayerController.asset('assets/video.mov');
final videoProvider = Provider<VideoPlayerController>((ref) => vpc
  ..initialize()
  ..setVolume(0)
  ..setLooping(true));

使用该Provider的Widget代码:

class VideoPage extends ConsumerWidget {
  const VideoPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
          title: const Text('Video Page'), backgroundColor: Colors.green),
      body: ListView(children: [
        Column(children: [
          AspectRatio(
            aspectRatio: 872 / 1320,
            child: VideoPlayer(ref.watch(videoProvider)),
          )
        ]),
        ElevatedButton(
            onPressed: () => ref.read(videoProvider).value.isPlaying
                ? vpc.pause()
                : vpc.play(),
            child: Icon(ref.read(videoProvider).value.isPlaying
                ? Icons.pause_circle
                : Icons.play_arrow))
      ]),
    );
  }
}

请问我哪里出错了?是不是不该直接调用vpc变量来控制播放/暂停?是否应该使用.update()方法更新视频状态?


问题分析与解决方案

问题根源

  1. 直接操作全局变量:你直接调用全局的vpc变量控制播放/暂停,但Riverpod无法感知到这个操作带来的状态变化,因此不会触发UI刷新。
  2. 普通Provider的局限性:Provider<VideoPlayerController>只会在初始化时创建一次实例,它无法监听VideoPlayerController内部的播放状态变化,自然不会通知UI更新。

修正方案(使用StateNotifierProvider)

不需要用StreamProvider,推荐用StateNotifierProvider来管理视频播放状态,它能更直观地追踪状态变化并触发UI更新。

1. 重构Provider,创建状态管理类

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:video_player/video_player.dart';

class VideoPlayerNotifier extends StateNotifier<bool> {
  final VideoPlayerController _controller;

  // 初始化时传入控制器,初始状态设为未播放
  VideoPlayerNotifier(this._controller) : super(false) {
    _initController();
    // 监听控制器的播放状态,自动更新state
    _controller.addListener(() {
      state = _controller.value.isPlaying;
    });
  }

  Future<void> _initController() async {
    await _controller.initialize();
    _controller.setVolume(0);
    _controller.setLooping(true);
  }

  // 统一的播放/暂停切换方法
  void togglePlayPause() {
    _controller.value.isPlaying ? _controller.pause() : _controller.play();
  }

  // 销毁时释放控制器资源
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

// 提供状态管理类的Provider
final videoPlayerProvider = StateNotifierProvider<VideoPlayerNotifier, bool>((ref) {
  final controller = VideoPlayerController.asset('assets/video.mov');
  return VideoPlayerNotifier(controller);
});

2. 修改Widget代码,通过Provider获取状态和操作方法

class VideoPage extends ConsumerWidget {
  const VideoPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 监听播放状态变化,自动刷新UI
    final isPlaying = ref.watch(videoPlayerProvider);
    // 获取状态管理类实例,用于调用操作方法
    final videoNotifier = ref.read(videoPlayerProvider.notifier);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Video Page'),
        backgroundColor: Colors.green,
      ),
      body: ListView(
        children: [
          AspectRatio(
            aspectRatio: 872 / 1320,
            child: VideoPlayer(videoNotifier._controller),
          ),
          ElevatedButton(
            onPressed: videoNotifier.togglePlayPause,
            child: Icon(isPlaying ? Icons.pause_circle : Icons.play_arrow),
          ),
        ],
      ),
    );
  }
}

为什么这样做?

  • StateNotifier专门用于管理可变状态,它会在state变化时自动通知所有监听的Widget刷新。
  • 通过统一的togglePlayPause方法操作播放状态,避免直接操作全局变量,确保状态变化被Riverpod追踪。
  • 控制器的资源释放被统一管理在dispose方法中,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 13:17:52