7

I'm building an app that basically is a YouTube clone. I use the official video_player plugin for playback and chewie for controls. I'd like to implement a quality switcher, so the user can decide what quality they want the video to be streamed at

I've built a bottom sheet with switches and I run changeQuality() when the user selects the desired quality. What it should do is simply giving a new source file to the old player and keep playing from where the video left.

This is the video player and chewie player that run on initState():

videoPlayer = VideoPlayerController.network(data == null
    ? dataAll[indexNo]["video"]["480"]
    : data[indexNo]["video"]["480"]);

chewieController = ChewieController(
    videoPlayerController: videoPlayer,
    aspectRatio: 16 / 9,
    autoPlay: true,
    allowedScreenSleep: false,
    placeholder: data == null
      ? Image(
      image: NetworkImage(dataAll[indexNo]["thumbnail"]),
      )
      : Image(
         image: NetworkImage(data[indexNo]["thumbnail"]),
      )
);

And the changeQuality() function:

changeQuality(String newQuality) {
  setState(() {
    position = videoPlayer.value.position;
    chewieController.pause();
    videoPlayer = new VideoPlayerController.network(data == null
      ? dataAll[indexNo]["video"]["$newQuality"]
      : data[indexNo]["video"]["$newQuality"]);
    chewieController = ChewieController(
      videoPlayerController: videoPlayer,
      aspectRatio: 16 / 9,
      autoPlay: true,
      allowedScreenSleep: false,
      startAt: position,
    );
  });
  Navigator.of(context).pop();
}

I've also tried disposing the old video player and then setting the new value, but I get an error that variables cannot be used after being disposed.

The switcher works a bit, because it changes the quality around 4 to 5 times and then it runs into an error and won't play anything.

Karol Wasowski
  • 465
  • 1
  • 6
  • 18

1 Answers1

5

I expand upon this solution for video_player and extend it to also cover chewie.

Key parts of this solution

  • You need two widgets. MyVideoPlayer that encapsulates video_player and chewie and an outer widget where you react to user input or state changes and swap out MyVideoPlayer with a new one.
  • This solution roundabouts the whole question in one way. I doesn't solve how to change video of video_player or chewie. Instead it follows the documented principal on how to use chewie for the whole life cycle of a host widget (MyVideoPlayer) and swap that one out to change video url.
  • You can stuff in more things in the outer widget as you see fit if you don't want to dedicate it just to containing MyVideoPlayer. Ie. if you want a description text adjacent to it based on app state.

Outer Widget

I write with this. but it can be omitted in Dart code.

class QuizVideoPlayer extends StatefulWidget {
  @override
  _QuizVideoPlayerState createState() => _QuizVideoPlayerState();
}

class _QuizVideoPlayerState extends State<QuizVideoPlayer> {
  Word _url;
  UniqueKey _urlKey;

  // Call this method from button or in reaction to model change etc.
  // I call it from Provider.of in didChangeDependencies, but I don't think it is
  // a necessary detail of the answer as it depends on how you do state management.
  // The key in this solution is that state management occur in the outer widget and
  // due to some trigger call _changeUrl() which changes _url and _urlKey which then
  // swaps out MyVideoPlayer.
  @override
  void _changeUrl(String newUrl) async {
    this.setState(() {
      // Rebuild MyVideoPlayer with a new instance => eventually dispose old controllers
      this._url = newUrl;
      this._urlKey = UniqueKey();
    });
  }

  @override
  Widget build(BuildContext context) {
    return 
      /* ... */
      this._url != null
          ? MyVideoPlayer(
              this._url,
              this._urlKey,
            )
          : AspectRatio(
              aspectRatio: 3 / 2,
              child: Container(color: Colors.black),
            )
      /* ... */
    );
  }
}

MyVideoPlayer

I write with this. but it can be omitted in Dart code.

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

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

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

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

class _MyVideoPlayerState extends State<MyVideoPlayer> {
  VideoPlayerController _controller;
  ChewieController _chewie;

  @override
  void initState() {
    this._initControllers(this.widget.videoUrl);
    super.initState();
  }

  void _initControllers(String url) {
    this._controller = VideoPlayerController.network(url);
    this._chewie = ChewieController(
      videoPlayerController: this._controller,
      autoPlay: true,
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return Chewie(controller: this._chewie);
  }
}
L--
  • 286
  • 5
  • 7