Better Player

pub package pub package pub package

Advanced video player based on video_player and Chewie. It's solves many typical use cases and it's easy to run.


This plugin is based on Chewie. Chewie is awesome plugin and works well in many cases. Better Player is a continuation of ideas introduced in Chewie. Better player fix common bugs, adds more configuration options and solves typical use cases.

✔️ Fixed common bugs
✔️ Added advanced configuration options
✔️ Refactored player controls
✔️ Playlist support
✔️ Video in ListView support
✔️ Subtitles support (HTML tags support)


  1. Add this to your pubspec.yaml file:
  better_player: ^0.0.10
  1. Install it
$ flutter packages get
  1. Import it
import 'package:better_player/better_player.dart';


Check Example project.

Basic usage

Create BetterPlayerDataSource and BetterPlayerController. You should do it in initState:

BetterPlayerController _betterPlayerController;

  void initState() {
    BetterPlayerDataSource betterPlayerDataSource = BetterPlayerDataSource(
    _betterPlayerController = BetterPlayerController(
        betterPlayerDataSource: betterPlayerDataSource);

Create BetterPlayer widget wrapped in AspectRatio widget:

  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 16 / 9,
      child: BetterPlayer(
        controller: _betterPlayerController,


To use playlist, you need to create dataset with multiple videos:

  List<BetterPlayerDataSource> createDataSet() {
    List dataSourceList = List<BetterPlayerDataSource>();
    return dataSourceList;

Then create BetterPlayerPlaylist:

  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 16 / 9,
      child: BetterPlayerPlaylist(
          betterPlayerConfiguration: BetterPlayerConfiguration(),
          betterPlayerPlaylistConfiguration: const BetterPlayerPlaylistConfiguration(),
          betterPlayerDataSourceList: dataSourceList),


BetterPlayerListViewPlayer will auto play/pause video once video is visible on screen with playFraction. PlayFraction describes percent of video that must be visibile to play video. If playFraction is 0.8 then 80% of video height must be visible on screen to auto play video

  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 16 / 9,
      child: BetterPlayerListVideoPlayer(
            BetterPlayerDataSourceType.NETWORK, videoListData.videoUrl),
        key: Key(videoListData.hashCode.toString()),
        playFraction: 0.8,

You can control BetterPlayerListViewPlayer with BetterPlayerListViewPlayerController. You need to pass BetterPlayerListViewPlayerController to BetterPlayerListVideoPlayer. See more here:


Subtitles can be configured from 3 different sources: file, network and memory. Subtitles source is passed in BetterPlayerDataSource:

Network subtitles:

    var dataSource = BetterPlayerDataSource(
      subtitles: BetterPlayerSubtitlesSource(
          type: BetterPlayerSubtitlesSourceType.NETWORK,

File subtitles:

 var dataSource = BetterPlayerDataSource(
      subtitles: BetterPlayerSubtitlesSource(
        type: BetterPlayerSubtitlesSourceType.FILE,
        url: "${directory.path}/",


You can provide configuration to your player when creating BetterPlayerController.

    var betterPlayerConfiguration = BetterPlayerConfiguration(
      autoPlay: true,
      looping: true,
      fullScreenByDefault: true,

Possible configuration options:

    /// Play the video as soon as it's displayed
    final bool autoPlay;

    /// Start video at a certain position
    final Duration startAt;

    /// Whether or not the video should loop
    final bool looping;

    /// Weather or not to show the controls when initializing the widget.
    final bool showControlsOnInitialize;

    /// When the video playback runs  into an error, you can build a custom
    /// error message.
    final Widget Function(BuildContext context, String errorMessage) errorBuilder;

    /// The Aspect Ratio of the Video. Important to get the correct size of the
    /// video!
    /// Will fallback to fitting within the space allowed.
    final double aspectRatio;

    /// The placeholder is displayed underneath the Video before it is initialized
    /// or played.
    final Widget placeholder;

    /// A widget which is placed between the video and the controls
    final Widget overlay;

    /// Defines if the player will start in fullscreen when play is pressed
    final bool fullScreenByDefault;

    /// Defines if the player will sleep in fullscreen or not
    final bool allowedScreenSleep;

    /// Defines the system overlays visible after exiting fullscreen
    final List<SystemUiOverlay> systemOverlaysAfterFullScreen;

    /// Defines the set of allowed device orientations after exiting fullscreen
    final List<DeviceOrientation> deviceOrientationsAfterFullScreen;

    /// Defines a custom RoutePageBuilder for the fullscreen
    final BetterPlayerRoutePageBuilder routePageBuilder;

    /// Defines a event listener where video player events will be send
    final Function(BetterPlayerEvent) eventListener;

    ///Defines subtitles configuration
    final BetterPlayerSubtitlesConfiguration subtitlesConfiguration;

    ///Defines controls configuration
    final BetterPlayerControlsConfiguration controlsConfiguration;


You can provide subtitles configuration with this class. You should put BetterPlayerSubtitlesConfiguration in BetterPlayerConfiguration.

 var betterPlayerConfiguration = BetterPlayerConfiguration(
      subtitlesConfiguration: BetterPlayerSubtitlesConfiguration(
        fontSize: 20,

Possible configuration options:

 ///Subtitle font size
  final double fontSize;

  ///Subtitle font color
  final Color fontColor;

  ///Enable outline (border) of the text
  final bool outlineEnabled;

  ///Color of the outline stroke
  final Color outlineColor;

  ///Outline stroke size
  final double outlineSize;

  ///Font family of the subtitle
  final String fontFamily;

  ///Left padding of the subtitle
  final double leftPadding;

  ///Right padding of the subtitle
  final double rightPadding;

  ///Bottom padding of the subtitle
  final double bottomPadding;


Configuration for player GUI. You should pass this configuration to BetterPlayerConfiguration.

ar betterPlayerConfiguration = BetterPlayerConfiguration(
      controlsConfiguration: BetterPlayerControlsConfiguration(

Possible configuration options:

  ///Color of the control bars
  final Color controlBarColor;

  ///Color of texts
  final Color textColor;

  ///Color of icons
  final Color iconsColor;

  ///Icon of play
  final IconData playIcon;

  ///Icon of pause
  final IconData pauseIcon;

  ///Icon of mute
  final IconData muteIcon;

  ///Icon of unmute
  final IconData unMuteIcon;

  ///Icon of fullscreen mode enable
  final IconData fullscreenEnableIcon;

  ///Icon of fullscreen mode disable
  final IconData fullscreenDisableIcon;

  ///Cupertino only icon, icon of skip
  final IconData skipBackIcon;

  ///Cupertino only icon, icon of forward
  final IconData skipForwardIcon;

  ///Flag used to enable/disable fullscreen
  final bool enableFullscreen;

  ///Flag used to enable/disable mute
  final bool enableMute;

  ///Flag used to enable/disable progress texts
  final bool enableProgressText;

  ///Flag used to enable/disable progress bar
  final bool enableProgressBar;

  ///Flag used to enable/disable play-pause
  final bool enablePlayPause;

  ///Progress bar played color
  final Color progressBarPlayedColor;

  ///Progress bar circle color
  final Color progressBarHandleColor;

  ///Progress bar buffered video color
  final Color progressBarBufferedColor;

  ///Progress bar background color
  final Color progressBarBackgroundColor;

  ///Time to hide controls
  final Duration controlsHideTime;

  ///Custom controls, it will override Material/Cupertino controls
  final Widget customControls;

  ///Flag used to show/hide controls
  final bool showControls;

  ///Flag used to show controls on init
  final bool showControlsOnInitialize;

  ///Control bar height
  final double controlBarHeight;

  ///Default error widget text
  final String defaultErrorText;

  ///Default loading next video text
  final String loadingNextVideoText;

  ///Text displayed when asset displayed in player is live stream
  final String liveText;

  ///Live text color;
  final Color liveTextColor;


Configure your playlist. Pass this object to BetterPlayerPlaylist

 var betterPlayerPlaylistConfiguration = BetterPlayerPlaylistConfiguration(
      loopVideos: false,
      nextVideoDelay: Duration(milliseconds: 5000),

Possible configuration options:

  ///How long user should wait for next video
  final Duration nextVideoDelay;

  ///Should videos be looped
  final bool loopVideos;


Define source for one video in your app.

    var dataSource = BetterPlayerDataSource(
      subtitles: BetterPlayerSubtitlesSource(
        type: BetterPlayerSubtitlesSourceType.FILE,
        url: "${directory.path}/",

Possible configuration options:

  ///Type of source of video
  final BetterPlayerDataSourceType type;

  ///Url of the video
  final String url;

  ///Subtitles configuration
  final BetterPlayerSubtitlesSource subtitles;

  ///Flag to determine if current data source is live stream
  final bool liveStream;


Define source of subtitles in your video:

 var subtitles = BetterPlayerSubtitlesSource(
        type: BetterPlayerSubtitlesSourceType.FILE,
        url: "${directory.path}/",

Possible configuration options:

  ///Source type
  final BetterPlayerSubtitlesSourceType type;

  ///Url of the subtitles, used with file or network subtitles
  final String url;

  ///Content of subtitles, used when type is memory
  final String content;

Listen to video events

You can listen to video player events like:


After creating BetterPlayerController you can add event listener this way:

      print("Better player event: ${event.betterPlayerEventType}");

Your event listener will ne auto-disposed on dispose time :)

More documentation