Jai Techie
Jai Techie

Reputation: 1957

Flutter Video Player On Click Pass URL dynamic and Load Video to play

sample screen of video player attached below How can i make video player to play dynamically , while onclick of list play video by passing the video URL?

while on click i have passed URL and tried to reinitialise and start play its not working ,state not changing

Here is my code,

videoplayerscreen.dart,

class VideoPlayerScreen extends StatefulWidget {
  int playBackTime;
  int playBackTotalTime;
  String setPlayTime;
  String setPlayDuration;
  double aspectRatio;
  String videoUrl;
  bool isForward;
  bool isFullScreen;
  bool allowFullScreen;
  bool showControls;
  bool isAutoPlay;
  int startWithinSeconds;

  VideoPlayerScreen({
    Key key,
    this.playBackTime = 0,
    this.playBackTotalTime = 0,
    this.setPlayTime = "00:00",
    this.setPlayDuration = "00:00",
    this.aspectRatio = 16 / 9,
    this.videoUrl =
        "",
    this.isForward = true,
    this.isFullScreen = false,
    this.allowFullScreen = false,
    this.showControls = true,
    this.isAutoPlay = false,
    this.startWithinSeconds = 0,
  }) : super(key: key);

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

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  VideoPlayerController _controller;

  void initPlayer() async {
    _controller = VideoPlayerController.network(widget.videoUrl);
    await _controller.initialize();
    _controller.setLooping(true);
    _controller.seekTo(Duration(seconds: widget.startWithinSeconds));
    if (widget.isAutoPlay) {
      _controller.play();
    }
    _controller.addListener(() {
      setState(() {
        widget.playBackTime = _controller.value.position.inSeconds;
        widget.setPlayTime = timeFormatter(widget.playBackTime);
      });
    });
  }

  @override
  void initState() {
    initPlayer();
    super.initState();
  }

 @override
  void dispose() {
    _controller.dispose();
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.landscapeRight,
      DeviceOrientation.landscapeLeft,
      DeviceOrientation.portraitUp,
      DeviceOrientation.portraitDown,
    ]);
    super.dispose();
  }

In Config,

video_player: ^0.10.5

Calling in Other class CourseDetails.dart ,

             Container(
                  padding: EdgeInsets.only(left: 20.0, right: 20.0),
                  child: ClipRRect(
                    borderRadius: BorderRadius.circular(8.0),
                    child: _playUrl != null
                        ? VideoPlayerScreen(videoUrl: _playUrl)
                        : Image.asset(
                            'assets/images/test_video_player_screen.png'),
                  ),
                )
GestureDetector(
 **here i am changing the state **
      onTap: () {
        setState(() {
            _playUrl = videoLists['course_video_url'];
        });
      },
      child: Padding(
        padding:
            EdgeInsets.only(left: 10.0, right: 20.0, top: 2.0, bottom: 5.0),
        child: Row(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Text(
              "${videoLists['sn_no']}",
              textAlign: TextAlign.center,
              style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                  color: Colors.black45,
                  fontFamily: 'Oswald-SemiBold'),
            ),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Padding(
                  padding: EdgeInsets.only(left: 10.0),
                  child: Text(
                    "${videoLists['course_video_title']}",
                    textAlign: TextAlign.left,
                    style: TextStyle(
                        fontSize: 14,
                        fontWeight: FontWeight.bold,
                        color: Colors.black45,
                        fontFamily: 'Oswald-SemiBold'),
                  ),
                ),
                Row(
                  children: <Widget>[
                    Padding(
                      padding: EdgeInsets.only(left: 10.0),
                      child: Text(
                        "Video - ${videoLists['course_video_duration']}",
                        textAlign: TextAlign.left,
                        style: TextStyle(
                            fontSize: 12,
                            fontWeight: FontWeight.bold,
                            color: Colors.grey,
                            fontFamily: 'Oswald-SemiBold'),
                      ),
                    ),
                    Padding(
                        padding: EdgeInsets.only(left: 5.0),
                        child: videoLists['course_watched']
                            ? Image.asset(
                                'assets/images/green_tick_icon.png',
                                width: 12.0,
                                height: 12.0,
                              )
                            : null),
                  ],
                ),
              ],
            ),
          ],
        ),
      ),
    )

Upvotes: 3

Views: 12869

Answers (5)

Abdur Rehman
Abdur Rehman

Reputation: 555

@Jai Techie I think you have already found the solution, But I will add some more explanation to it for those who still have got some errors using the video_player package.

Actually, when we are loading another video from the list of videos using network URLs, we create another instance of our videoController and initialize it without destroying the previous one.

Another big issue is that The video_player plugin fails to play videos after playing successfully 10-15 times stated here on GitHub video_player issue/24565.

First of all, we will use a Chewie Wrapper Package which is a wrapper package around the video_player package.

 /// initialize the videoController
  void _initController(String link) {
   _videoController = VideoPlayerController.network(link)
    ..initialize().then((_) {
      setState(() {});
    //do what you want.
      _videoController?.play();
  });
chewieController = ChewieController(
    videoPlayerController: _videoController!,
);
}

 Future<void> _startVideoPlayer(String link) async {
  if (_videoController == null && chewieController == 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 = _videoController;
    final oldChewieController = chewieController;

    // Registering a callback for the end of the next frame
    // to dispose of an old controller
    // (which won't be used anymore after calling setState)

  WidgetsBinding.instance.addPostFrameCallback((_) async {
    await oldController?.dispose();
    oldChewieController?.dispose();

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

  //Make sure that the controller is not used by setting it to null

  setState(() {
    _videoController = null;
  });

}
}

Now call this method from your initState

_startVideoPlayer('your URL here')

And also call it from your listview (list of videos) onTap property

_startVideoPlayer(listOfVideos[index].URLS.toString())

Just Create a method _startVideoPlayer() where it will check if videoController is initialized or not. If videoController is not initialized it will call the initController(), if it is already initialized, first we have to dispose() of that videoController after disposing of it then initialize the new one.

I hope it will work.

Upvotes: 0

nicks101
nicks101

Reputation: 1201

You can use didUpdateWidget method.

We can compare any property of the Widget class and according to that reset the player.

For resetting, general steps would be

  1. stop the player
  2. close the session
  3. initialize player again (with new URL and other parameters)

Below is the code snippet for logic.

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  VideoPlayerController _controller;

  void initPlayer() async {
    /// initialise player
  }

  void disposePlayer() {
    /// dispose player
  }

  void resetPlayer() {
    disposePlayer();
    initPlayer();
  }

  @override
  void initState() {
    initPlayer();
    super.initState();
  }

  @override
  void didUpdateWidget(VideoPlayerScreen oldWidget) {
    super.didUpdateWidget(oldWidget);

    if (oldWidget.videoUrl != widget.videoUrl) {
      resetPlayer();
    }
  }

  @override
  void dispose() {
    disposePlayer();
    super.dispose();
  }
}

Reference for didUpdateWidget method.

Upvotes: 0

Jai Techie
Jai Techie

Reputation: 1957

Thanks for all the replies. I got the solution:

In Build Function, I have checked if any url changes happened or not. If changes happened, I disposed old controller and I reinitialized video controller. Then, it works fine for me.

@override
Widget build(BuildContext context) {
    if (currentObjectVideoUrl != widget.videoUrl) {
       _controller.dispose();
       initPlayer();
    }
}

Upvotes: 4

K K
K K

Reputation: 996

You can achieve this by creating a stateful widget passing a URL. Then inside the init method, you can do this

class ViewVideo extends StatefulWidget {
  final link;
  ViewVideo(this.link);
  @override
  _ViewVideoState createState() => _ViewVideoState(this.link);
}

class _ViewVideoState extends State<ViewVideo> {
  final link;
  _ViewVideoState(this.link);

  @override
  void initState() {
    super.initState();
    //Write your video player code here
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(),
    );
  }

  @override
  void dispose() {
     //Here you can close the video player
     super.dispose();
  }
}

Upvotes: 1

J. S.
J. S.

Reputation: 9635

I've modified my response based on your comments. You just need to have your video url on a class variable that you change with the setState. Then on your Widgets have a check for the presence of the variable and decide to show the VideoPlayerScreen widget or an empty box.

String videoUrl = '';

@override
Widget build(BuildContext context) {
  return Column(
    children: <Widget>[
      videoUrl == '' ? SizedBox() : VideoPlayerScreen(videoUrl: videoUrl),
      RaisedButton(
        onPressed: () {
          setState((){
            videoUrl == videoLists['course_video_url'];
          });
        }
      ),
    ],
  );
}

Upvotes: 0

Related Questions