简体   繁体   English

Flutter video_player 处置

[英]Flutter video_player dispose

I'm using provider with video_player to manage the state.我正在使用带有video_playerprovider来管理 state。

VideoProvider has: VideoProvider 有:

videoPlayerController = VideoPlayerController.network(...);

which I frequently change when the user switches to a new video (same screen).当用户切换到新视频(同一屏幕)时,我经常更改它。 If I directly assign a new VideoPlayerController.network(...) into the videoPlayerController , the old video will still play and you can hear the sound.如果我直接将 new VideoPlayerController.network(...)分配给videoPlayerController ,旧视频仍会播放并且您可以听到声音。 The workaround is to videoPlayerController.pause() then assign a new VideoPlayerCOntroller.network afterwards.解决方法是使用videoPlayerController.pause()然后分配一个新的VideoPlayerCOntroller.network

Is the previous Video being disposed by GC?以前的视频是否正在被 GC 处理? Are there any performance issues with this?这有什么性能问题吗? I want to get rid of the previous video and the resources used before switching to a new one.我想在切换到新视频之前摆脱以前的视频和使用的资源。 I can't videoPlayerController.dispose() before switching because it causes an error.我不能在切换之前使用videoPlayerController.dispose() ,因为它会导致错误。

When you call dispose your controller is still being used by VideoPlayer widget.当您调用dispose时,您的 controller 仍在被VideoPlayer小部件使用。 First, you need to make sure that it's not used anymore (setting controller in state to null) and AFTER that you call dispose.首先,您需要确保不再使用它(将 state 中的 controller 设置为 null)并在调用 dispose 之后。

I'm not sure about your state management via Provider, but I'll give you an example how to do this with regular State.我不确定你的 state 通过 Provider 管理,但我会给你一个例子,如何使用常规 State 做到这一点。

  VideoPlayerController _controller;

  void _initController(String link) {
    _controller = VideoPlayerController.network(link)
      ..initialize().then((_) {
        setState(() {});
      });
  }

  Future<void> _onControllerChange(String link) async {
    if (_controller == null) {
      // If there was no controller, just create a new one
      _initController(link);
    } else {
      // If there was a controller, we need to dispose of the old one first
      final oldController = _controller;

      // Registering a callback for the end of next frame
      // to dispose of an old controller
      // (which won't be used anymore after calling setState)
      WidgetsBinding.instance.addPostFrameCallback((_) async {
        await oldController.dispose();

        // Initing new controller
        _initController(link);
      });

      // Making sure that controller is not used by setting it to null
      setState(() {
        _controller = null;
      });
    }
  }

I know its late but this will help others with same problem.我知道它很晚,但这将帮助其他有同样问题的人。

I had same issue, i wanted to change Video when user selects new video from list, after researching so much i finally created a solution by myself.我有同样的问题,当用户从列表中选择新视频时,我想更改视频,经过大量研究,我终于自己创建了一个解决方案。

Follows the below step and you can video video in Video Player in same screen按照以下步骤,您可以在同一屏幕上的视频播放器中播放视频

  1. Create a StatefulWidget class containing VideoPlayer and initialize the videoPlayerController in init method.创建一个包含VideoPlayerStatefulWidget class 并在init方法中初始化 videoPlayerController。 Suppose you created a StatefulWidget class named MyVideoPlayer then in constructor accept two variable, ie, i> String videoLink ii> UniqueKey()假设您创建了一个名为 MyVideoPlayer 的 StatefulWidget class 然后在构造函数中接受两个变量,即 i> String videoLink ii> UniqueKey()

Note - You have to pass UniqueKey() to the super of this class, example,注意- 您必须将 UniqueKey() 传递给此 class 的上级,例如,

class MyVideoPlayer extends StatefulWidget {
  final String videoLink;
  final UniqueKey newKey;

  MyVideoPlayer(this.videoLink, this.newKey): super(key: newKey); // passing Unique key to dispose old class instance and create new with new data

  @override
  _MyVideoPlayerState createState() => _MyVideoPlayerState();
} ...
  1. Replace your VideoPlayer instance of main dart file where you are playing video with your previously created class ie, MyVideoPlayer and pass videoLink and UniqueKey() .将您正在播放视频的主 dart 文件的VideoPlayer实例替换为您之前创建的 class 即MyVideoPlayer并传递 videoLink 和UniqueKey()

  2. Now whenever you want to change the video just update videoLink inside setState(() {}) of your main dart file and after that a new VideoPlayer instance is created by disposing the old one totally.现在,每当您想更改视频时,只需更新主 dart 文件的setState(() {})内的 videoLink,然后通过完全处理旧的 VideoPlayer 实例创建一个新的 VideoPlayer 实例。

Note - Here the main work is done by UniqueKey(), by passing UniqueKey() you are saying flutter to create a new unique instance of this particular class;注意 - 这里的主要工作是由 UniqueKey() 完成的,通过传递 UniqueKey() 你说 flutter 来创建这个特定 class 的新唯一实例;

You can use that:你可以使用它:

@override
  void dispose() {
    if (_controller.value.isPlaying) _controller.pause();
    _controller.removeListener(_videoListener);
    _controller = null;
    super.dispose();
  }

I know is later but I think i found one way to fix this.我知道是以后,但我想我找到了解决这个问题的一种方法。

1.Create initPlayer() (I've created using Provider ) 1.创建initPlayer() (我使用Provider创建)

  void initPlayer( {pause = false, update = false, updateUrl = ""} ) async {
  var isPlaying = false;

  if (controller == null) {
    controller = VideoPlayerController.file(File(url))..addListener(() {
    isPlaying = true;
    notifyListeners();
   })..initialize().then((value) => notifyListeners());
   controller.play();
   } else {
     print ("Controller is playing...");
   if (pause) {
    if (controller.value.isPlaying) {
      controller.pause();
    } else {
      controller.play();
    }
   }
   if (update && updateUrl != "") {
    final oldController = controller;
    await oldController.dispose();
    controller = VideoPlayerController.file(File(updateUrl))..addListener(() {
      isPlaying = true;
      notifyListeners();
       })..initialize().then((value) => notifyListeners());
      controller.play();
    }
  }
}
  1. Add Actions ( start , pause change ) in the widget.在小部件中添加Actionsstartpause change )。

     child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ FlatButton( child: Text("Play Video"), color: Colors.red, onPressed: () { musicPlayerProvider.initPlayer(); }, ), FlatButton( child: Text("Stop Video"), color: Colors.pink, onPressed: () { musicPlayerProvider.initPlayer(pause: true); }, ), FlatButton( child: Text("Change Video"), color: Colors.yellow, onPressed: () { musicPlayerProvider.initPlayer(update: true,updateUrl: "url"); }, ), ],),

For now this works but of course you can make changes.目前这works ,但您当然可以进行更改。

Later we need to check when the video has finished using addListener method ( I guess ).稍后我们需要使用addListener方法检查video何时finished (我猜)。

This one fixed my issue thanks to someone from this link感谢此链接中的某人,这解决了我的问题

void dispose() {
        _controller.pause();
        _controller?.dispose();
        //ignore below
        audioPlayer.stop();
        audioPlayer?.dispose();
        super.dispose();
      }

I was making a-la Instagram grid view.我正在制作 Instagram 网格视图。 Every time when I picked a new file I should update my thumbnail.每次我选择一个新文件时,我都应该更新我的缩略图。 But my Stateful widget was not recognizing the update.但是我的有状态小部件无法识别更新。 The dispose method wasn't launching. dispose 方法没有启动。

Finally I came up with this.最后我想到了这个。 Hope this will help someone.希望这会对某人有所帮助。

class _SelectedVideo extends StatefulWidget {
  const _SelectedVideo({
    Key? key,
    required this.file,
  }) : super(key: key);
  final File file;

  @override
  State<_SelectedVideo> createState() => _SelectedVideoState();
}

class _SelectedVideoState extends State<_SelectedVideo> {
  VideoPlayerController? _controller;

  void _initVideoPlayer(File file) async {
    _controller = VideoPlayerController.file(
      file,
    );
    await _controller!.initialize();
    _controller!.setVolume(0);
    _controller!.play();

    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    _initVideoPlayer(widget.file);
  }

  @override
  void didUpdateWidget(covariant _SelectedVideo oldWidget) {
    // I'm disposing controller all the time when widget updates
    _controller?.dispose();

    _initVideoPlayer(widget.file);
    super.didUpdateWidget(oldWidget);
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_controller == null) return Container();
    return Container(
      color: Colors.black,
      height: 375,
      width: double.infinity,
      child: FittedBox(
        fit: BoxFit.cover,
        clipBehavior: Clip.hardEdge,
        child: SizedBox(
          width: _controller!.value.size.width,
          height: _controller!.value.size.height,
          child: AspectRatio(
            aspectRatio: _controller!.value.aspectRatio,
            child: VideoPlayer(_controller!),
          ),
        ),
      ),
    );
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM