首页 > 解决方案 > Flutter 无法处理 VideoPlayerController

问题描述

在处理完我的 VideoPlayerController 后使用后退按钮时出现异常。

我的 VideoPlayer 和 VideoPlayerController 设置如下:

 String url;
 var plan;
_VideoPlayerScreenState(this.url,{this.plan});

 Future<Null> OnWillPop(){

_controller.dispose();

if(plan!=null)
Navigator.push(context, MaterialPageRoute(builder: (context)=>
ListSession(plan :plan)));
else
  Navigator.push(context, MaterialPageRoute(builder: (context)=>
ListMoves()));

}
VideoPlayerController _controller;
Future<void> _initializeVideoPlayerFuture;

@override
void initState() {
 _controller = VideoPlayerController.network(
  url,
 );

// Initialize the controller and store the Future for later use.
_initializeVideoPlayerFuture = _controller.initialize();

// Use the controller to loop the video.
_controller.setLooping(true);
_controller.play();
super.initState();
}

@override
void dispose() {
 print("+++++++++++++++++++++++++++++++++++++++++++");
_controller.dispose();
 super.dispose();
}

 @override
 Widget build(BuildContext context) {
 return WillPopScope(onWillPop: OnWillPop,child:Scaffold(

   // Use a FutureBuilder to display a loading spinner while waiting for the
   // VideoPlayerController to finish initializing.
   body: FutureBuilder(
    future: _initializeVideoPlayerFuture,
    builder: (context, snapshot) {
      if (snapshot.connectionState == ConnectionState.done) {
        // If the VideoPlayerController has finished initialization, use
        // the data it provides to limit the aspect ratio of the video.
        return Center(
          child: AspectRatio(
            aspectRatio: _controller.value.aspectRatio,
            // Use the VideoPlayer widget to display the video.
            child: VideoPlayer(_controller),
          ),
        );
      } else {
        // If the VideoPlayerController is still initializing, show a
        // loading spinner.
        return Center(child: CircularProgressIndicator());
      }
    },
  ),
 ));
}

但是当我按下后退按钮时,我得到了这个错误:

Another exception was thrown: A VideoPlayerController was used after being disposed.

如何正确处理我的 VideoPlayerController 并能够合并后退按钮?

标签: flutterdartdispose

解决方案


您可以在下面复制粘贴运行完整代码
并标记取消标记Navigator.push/Navigator.pushReplacement以检查效果
在您的情况下,您不需要处理controller,因为controller是此页面的本地,您可以直接执行_controller.pause()
您使用Navigator.push转到下一页意味着您pop将从下一页
如果你dispose controller设置controllernull成功,当从下一页弹出时,你会得到一个错误,因为initState不会再次调用,controller不会再次初始化

如果你使用Navigator.pushReplacement,dispose会被自动调用,
你可以在演示代码中看到控制台 show _controller.dispose。


从页面返回时可以看到工作演示ListMove,视频仍然可以播放

在此处输入图像描述

完整代码

import 'dart:async';

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

void main() => runApp(VideoPlayerApp());

class VideoPlayerApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Video Player Demo',
      home: VideoPlayerScreen(),
    );
  }
}

class VideoPlayerScreen extends StatefulWidget {
  VideoPlayerScreen({Key key}) : super(key: key);

  @override
  _VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  VideoPlayerController _controller;
  VideoPlayerController _oldController;
  Future<void> _initializeVideoPlayerFuture;

  @override
  void initState() {
    print("initState");
    // Create and store the VideoPlayerController. The VideoPlayerController
    // offers several different constructors to play videos from assets, files,
    // or the internet.
    _controller = VideoPlayerController.network(
      'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
    );

    // Initialize the controller and store the Future for later use.
    _initializeVideoPlayerFuture = _controller.initialize();

    // Use the controller to loop the video.
    _controller.setLooping(true);

    super.initState();
  }

  @override
  void dispose() {
    print("_controller.dispose");
    // Ensure disposing of the VideoPlayerController to free up resources.
    //_initializeVideoPlayerFuture = null;
    _controller.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        await _controller.pause();
        Navigator.push(
            context, MaterialPageRoute(builder: (context) => ListMoves()));

        /*Navigator.pushReplacement(
            context, MaterialPageRoute(builder: (context) => ListMoves()));*/
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('Butterfly Video'),
        ),
        // Use a FutureBuilder to display a loading spinner while waiting for the
        // VideoPlayerController to finish initializing.
        body: FutureBuilder(
          future: _initializeVideoPlayerFuture,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.done) {
              // If the VideoPlayerController has finished initialization, use
              // the data it provides to limit the aspect ratio of the video.
              return AspectRatio(
                aspectRatio: _controller.value.aspectRatio,
                // Use the VideoPlayer widget to display the video.
                child: VideoPlayer(_controller),
              );
            } else {
              // If the VideoPlayerController is still initializing, show a
              // loading spinner.
              return Center(child: CircularProgressIndicator());
            }
          },
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            // Wrap the play or pause in a call to `setState`. This ensures the
            // correct icon is shown.
            setState(() {
              // If the video is playing, pause it.
              if (_controller.value.isPlaying) {
                _controller.pause();
              } else {
                // If the video is paused, play it.
                _controller.play();
              }
            });
          },
          // Display the correct icon depending on the state of the player.
          child: Icon(
            _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
          ),
        ), // This trailing comma makes auto-formatting nicer for build methods.
      ),
    );
  }
}

class ListMoves extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('List Movies'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Launch screen'),
          onPressed: () {},
        ),
      ),
    );
  }
}

推荐阅读