# better_player: a federated Flutter video player that replaces video_player, chewie and a cache plugin

> better_player is a Flutter video package split into per platform packages that sit on Media3, AVPlayer, Shaka Player and libmpv, bundling the controller, caching, DRM, subtitles, PiP and an ad engine into one dependency. The bet is that one package with native engines beats stitching several together, and the cost is a migration and a smaller web platform support story than media_kit.

**jhomlala/betterplayer** — Better video player for Flutter, with multiple configuration options. Solving typical use cases!

- Repository: https://github.com/jhomlala/betterplayer
- Stars: 1,027 · Forks: 1,397
- Language: Dart
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/jhomlala-betterplayer

## One dependency instead of four, which is the entire pitch

The README opens with a problem statement rather than a feature list. Stop stitching video_player, separate UI packages, abandoned caching plugins, and custom DRM code together. Better Player packs the full video stack into a single, cohesive engine.

That framing matters because the Flutter video story has been fragmented for a long time. The official video_player package gives you a controller and no interface, chewie gives you the interface and no pipeline, caching has typically meant a separate third party package, and DRM has meant native code. Each piece is fine; the assembly is the problem, and the assembly is where the bugs live.

The comparison table in the README makes the argument in a grid, and the rows are the interesting part. App size overhead is listed at roughly 1 to 2 MB for Better Player using native OS engines, the same range for the video_player and chewie pairing, and plus 30MB to 50MB for media_kit because of bundled mpv binaries. HLS and DASH are full native support here, basic for video_player with chewie, and native for media_kit. DRM with Widevine and FairPlay is native support here and none for the other two. Smart caching is built in, and listed as needing a third party package for the video_player route and absent for media_kit. Subtitles are SRT, WebVTT and TTML here, SRT only for video_player with chewie, and advanced for media_kit.

The rest of the table continues the same logic. Picture in Picture is built in, absent for video_player and chewie and platform dependent for media_kit. Playlists are built in and absent for video_player. The VAST and VMAP ad support, a client side ad engine covering VAST 2.0 through 4.x and VMAP, is listed as none for both alternatives. And the video feeds and pooling feature, with the BetterPlayerFeed and BetterPlayerPool types, is listed as built in and none elsewhere.

The size row is the one to hold onto, because it is the clearest structural difference. Bundling an mpv binary into every app is a large fixed cost that most teams would rather avoid when the operating system already ships a media engine.

## A federated plugin, with a different engine behind each platform

Better Player uses a federated architecture, and each platform runs on its native, hardware accelerated media engine. The platform table names them.

Android uses better_player_android on Google Media3, which is the ExoPlayer lineage. iOS uses better_player_ios on Apple AVPlayer. macOS uses better_player_macos, also on Apple AVPlayer. Web uses better_player_web on Shaka Player and HTML5. Windows uses better_player_windows on libmpv.

That is five packages behind one Dart facing API, and the design is the interesting part. On a phone, the system video engine is what you want, because it is hardware accelerated, it integrates with the platform's audio focus handling, it plays the formats the device supports, and it costs no binary size. Shaka Player on the web is the same idea taken to the browser, since the browser already has a media element and you add a library rather than an engine. libmpv on Windows is the exception that proves the rule: here the engine itself ships with the app.

The tradeoff this creates is worth being explicit about. Because the engines differ, capability is not uniform. Widevine on Android, FairPlay on Apple, MP4 and HLS on web, and whatever libmpv supports on Windows are not the same set. A feature that works on one platform may not exist on another, and the API is a common denominator over five different backends.

The web entry is the one to examine carefully for a Flutter product. Shaka Player is a capable library, and it is a library rather than a native engine, so the web version has its own limitations, especially around DRM and offline caching in a browser context.

## Two ways to play, and the controller that carries the configuration

The quick start shows a deliberately small path first. Installing the package is one command.

```bash
flutter pub add better_player
```

Then a network video with full controls takes three lines, using the AspectRatio widget to hold the 16 by 9 aspect.

```dart
import 'package:better_player/better_player.dart';

AspectRatio(
  aspectRatio: 16 / 9,
  child: BetterPlayer.network(
    "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
  ),
)
```

For production use the shape changes, because the configuration moves into a controller and the data source is described separately. Caching, custom headers and DRM are all set on the data source, and the controller is created from a PlayerConfiguration and that data source.

```dart
final controller = BetterPlayerController(
  const PlayerConfiguration(
    aspectRatio: 16 / 9,
    autoPlay: true,
  ),
  betterPlayerDataSource: PlayerDataSource(
    DataSourceType.network,
    "https://example.com/stream.m3u8",
    // Cache video locally for instant playback:
    cacheConfiguration: const CacheConfiguration(useCache: true),
    // Or protect content with DRM:
    drmConfiguration: DrmConfiguration(
      drmType: DrmType.widevine,
      licenseUrl: "https://license.example.com",
    ),
  ),
);

BetterPlayer(controller: controller);
```

The design point is that the same controller carries playback state and the data source carries the content description, so switching between a cached file, a plain network URL and a DRM protected stream is a change of data source rather than a different widget. The DRM configuration names a type and a license URL, which is the minimal shape a Widevine or FairPlay setup needs.

The controller is also the thing you hold across rebuilds. The migration guide for video_player says to replace VideoPlayerController and remove the separate UI packages, which tells you the controller in this package absorbs the role that was previously split between the media controller and the chewie widget, and that chewie users are pointed at consolidating the player and the controls into a single controller.

## Documentation, a live demo, and an example app you can read

The project publishes several things beyond the package, and for a package of this size they matter.

There is official documentation hosted at jhomlala.github.io/betterplayer, described as full guides for caching, DRM, subtitles, VAST ads and playlists. There is a live demo at the demo path of the same site. There is an example application in the repository under packages/better_player_example. And there are two written migration guides, one from video_player and one from chewie, both described as step by step.

The presence of a separate example app package is worth noting. It means the package set is a monorepo of published packages rather than a single pubspec, which is what a federated plugin needs anyway, and it gives you a real application to read rather than a snippet in a README.

The repository layout is consistent with that. The top level holds packages/, docs/, e2e/, rules/, scripts/, assets/, a pubspec.yaml and pubspec.lock, a package.json, an analysis_options.yaml, an AGENTS.md and a LICENSE. The rules/ directory suggests the lint rules the project applies to itself, and the e2e/ directory suggests end to end tests are part of the workflow rather than an afterthought. The package.json at the root is not the Dart package, it is the documentation site, with Docusaurus 3.10 and React 18, and scripts for start, build, serve, deploy, swizzle and clear.

That last detail is a small indicator of how the project is run. The docs are built by a Node toolchain while the package is Dart, so a contributor needs both, and the two are kept in separate root files rather than fighting over one manifest.

## Dart package skills, which is a new kind of onboarding

There is a section on AI coding agents, and it is short enough to miss.

Better Player supports Dart package skills so that tools like Cursor, Claude Code, Gemini and Copilot understand the API out of the box. The command to fetch them is one line.

```bash
dart run skills@ get -p better_player
```

This is a new mechanism and it is worth understanding what it does and does not do. A Dart package skill is a set of instructions and reference material that a coding agent loads for a given package, so the agent knows the real API surface, the configuration options, the platform differences and the migration paths, rather than guessing from the README or from a training set that predates the package.

For a package with five platform implementations, per platform capabilities and a controller and data source split, that is a real problem. An agent that has only seen the quick start will write code that assumes behaviour is uniform across platforms, which is exactly the assumption that breaks.

The AGENTS.md file at the repository root points the same way, an agent oriented instruction file living alongside the code.

There is a fair caveat. A skill gives the agent the documented API. It does not tell it which engine a given platform uses, and it does not remove the need to test on the device you ship to. But as a mechanism for keeping generated code inside the real API, it is a better starting point than whatever the model happens to remember about Flutter video packages, which is often the old video_player and chewie pairing.

The same reasoning applies to the migration guides, which serve humans doing the same job by hand.

## The limits of a common denominator over five engines

The honest constraints follow from the architecture, and the comparison table is where most of them are visible.

The first is platform coverage. There are five platform packages, Android, iOS, macOS, web and Windows. There is no Linux package in the list, which is a real gap for a Flutter project targeting desktop Linux.

The second is the uniform API limit. Because the backends are Media3, AVPlayer, Shaka Player and libmpv, the API can only promise what all of them promise. PiP is described as native in the table for this package and platform dependent for media_kit, and DRM is native here and none for the other two, which means a project that needs DRM on every platform including web is in a different position from one that needs it on mobile only.

The third is the web story. Shaka Player is a capable library, and it is a library rather than a native engine, so the web version has its own limitations, especially around DRM and offline caching in a browser context. If your application is primarily a web application, this is where the comparison with media_kit deserves the most attention, because media_kit's weakness is binary size and its strength is uniform behaviour.

The fourth is a simple one about risk. A package that replaces four dependencies replaces four sets of maintenance. If the maintainer stops, you have one unmaintained dependency instead of four, which is better. If a platform's engine changes underneath you, you have one place to react. The package is a single point of failure either way, and the 1.24.0 version with a push on 2026-10-19 suggests it is being maintained actively, which is the relevant fact for a dependency decision.

## Conclusion

better_player fits a Flutter team that has been running video_player plus chewie plus a caching plugin plus hand written DRM code and wants one dependency with a controller, and it fits a team shipping DRM protected or ad supported content without writing a native module. It does not fit a team that needs a uniform API across every platform including Linux and mobile web, because the platform coverage is Android, iOS, macOS, web and Windows, with each platform on a different engine. Check three things before you commit. Check the engine on the platform you care about, because Windows runs libmpv while Android runs Media3 and iOS and macOS run AVPlayer, so behaviour and format support differ per platform. Check the migration, because there are written guides for moving off video_player and off chewie, which tells you both are common starting points and that the API is not a drop in. And check your subtitle formats, because SRT, WebVTT and TTML are supported while the comparison table lists SRT only for the video_player pairing. The last push was on 2026-10-19, the newest release is 1.24.0 from 2026-10-11, and the license is Apache-2.0.

## FAQ

### How do I add better_player to a Flutter project?

Run flutter pub add better_player, then import package:better_player/better_player.dart. A basic network video is an AspectRatio with a BetterPlayer.network child, and production use goes through BetterPlayerController with a PlayerConfiguration and a PlayerDataSource.

### Which media engines does better_player use per platform?

The package uses a federated architecture with better_player_android on Google Media3, better_player_ios and better_player_macos on Apple AVPlayer, better_player_web on Shaka Player and HTML5, and better_player_windows on libmpv.

### Does better_player support DRM and caching?

Yes. DrmConfiguration takes a type such as DrmType.widevine and a license URL, and CacheConfiguration with useCache set to true caches video locally for instant playback. Widevine and FairPlay are described as native support.

### How do I migrate from video_player or chewie to better_player?

The project publishes separate step by step guides for each. The video_player guide covers replacing VideoPlayerController and removing separate UI packages, and the chewie guide covers consolidating the player and its controls into a single controller.

### What is the app size overhead of better_player?

The comparison table lists roughly 1 to 2 MB using native OS engines, the same range for the video_player and chewie pairing, and plus 30MB to 50MB for media_kit because of bundled mpv binaries.

### What license is better_player released under?

Apache-2.0, with a LICENSE file at the root of the repository. The newest listed release is 1.24.0 from 2026-10-11, and the last push was on 2026-10-19.

## Sources

- [Issues](https://github.com/jhomlala/betterplayer/issues)
- [jhomlala/betterplayer on GitHub](https://github.com/jhomlala/betterplayer)
- [License: Apache-2.0](https://github.com/jhomlala/betterplayer/blob/master/LICENSE)
- [README](https://github.com/jhomlala/betterplayer/blob/master/README.md)
- [Releases](https://github.com/jhomlala/betterplayer/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jhomlala-betterplayer
