Flutter Video Playback: When to Choose `video_player` vs. `media_kit` (and APK Size Concerns)
The Flutter news you actually need
No spam, ever. Unsubscribe in one click.
So your Flutter app needs to play videos. You reach for the most common package, video_player, but then you hear about media_kit with its promises of broader format support. Which one should you choose? The decision isn’t just about API preference—it can drastically affect your app’s size, performance, and ability to handle complex video workloads. Let’s break down the key differences to help you pick the right tool.
The Core Contenders
video_player is the official, first-party plugin maintained by the Flutter team. It provides a basic, reliable wrapper around the platform’s native video playback capabilities (ExoPlayer on Android, AVPlayer on iOS/macOS). It’s the “batteries-included” choice for standard use cases.
media_kit is a powerful third-party package that uses libmpv under the hood—a highly portable, feature-rich media player library. It brings desktop support (Windows, Linux, macOS) and advanced features to all platforms, often at the cost of increased app size.
Key Comparison: Features & Limitations
Here’s the practical trade-off:
- Format & Codec Support:
video_playerdepends on what the underlying OS supports. This is usually fine for common formats (MP4, H.264) but can fail with specialty codecs or containers (like MKV with ASS subtitles).media_kit, vialibmpvand bundled FFmpeg, can play almost anything you throw at it. - Hardware Decoder Limits: This is a critical, often overlooked constraint. Mobile OSes impose a strict limit on the number of concurrent hardware decoders. If your app needs to play multiple videos simultaneously (e.g., in a grid, or a video feed with previews),
video_playercan quickly hit this ceiling, causing crashes or silent failures.media_kitcan often work around this by using software decoding or more efficient resource management. - Advanced Features: Need frame-perfect seeking, custom shaders for video filters, or complex subtitle rendering?
media_kitis built for this.video_playeroffers a simpler, more constrained API.
The App Size Elephant in the Room
This is where the choice becomes very tangible. Let’s look at the impact on your build.
video_player is incredibly lightweight for your APK/IPA. It adds almost nothing, as it relies on system libraries. Your final binary size remains largely unaffected.
media_kit is heavy. It bundles libmpv and often FFmpeg shared libraries. As noted in community discussions, this can lead to an APK size increase of 50-150 MB. After installation on a device, the extracted native libraries can consume 600-800 MB of storage. For mobile, this is a massive commitment.
Code Example: Basic Implementation
Let’s see how a simple playback setup differs.
Using video_player:
import 'package:video_player/video_player.dart';
import 'package:flutter/material.dart';
class SimpleVideoPlayer extends StatefulWidget {
const SimpleVideoPlayer({super.key});
@override
State<SimpleVideoPlayer> createState() => _SimpleVideoPlayerState();
}
class _SimpleVideoPlayerState extends State<SimpleVideoPlayer> {
late VideoPlayerController _controller;
@override
void initState() {
super.initState();
// Initialize from a network source
_controller = VideoPlayerController.networkUrl(
Uri.parse('https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'),
)..initialize().then((_) {
setState(() {});
_controller.play();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _controller.value.isInitialized
? AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
)
: const CircularProgressIndicator(),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Using media_kit:
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
class AdvancedVideoPlayer extends StatefulWidget {
const AdvancedVideoPlayer({super.key});
@override
State<AdvancedVideoPlayer> createState() => _AdvancedVideoPlayerState();
}
class _AdvancedVideoPlayerState extends State<AdvancedVideoPlayer> {
late final Player _player;
late final VideoController _videoController;
@override
void initState() {
super.initState();
// Initialize the player
_player = Player();
_videoController = VideoController(_player);
// Open a media source
_player.open(Media('https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'));
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Video(
controller: _videoController,
),
),
);
}
@override
void dispose() {
_player.dispose();
super.dispose();
}
}
The media_kit API is more object-oriented, separating the Player (logic) from the Video (widget).
Decision Guide: Which One When?
Choose video_player if:
- You’re building a typical mobile app (iOS/Android).
- You play common video formats (MP4, WebM) one at a time.
- App size is a major concern (e.g., for user acquisition).
- You don’t need advanced playback features.
Choose media_kit if:
- You require desktop support (Windows, Linux, macOS).
- Your app must play multiple videos concurrently and you fear hardware decoder limits.
- You need support for exotic codecs, containers, or advanced subtitle formats.
- You’re building a video-centric app (like a downloader, player, or editor) where rich features justify the large binary size.
- Crucially, you are already bundling FFmpeg (e.g., via
ffmpeg_kit_flutter). In this case,media_kitcan sometimes share these existing native dependencies, mitigating the total size impact.
Common Mistake: Not Planning for Concurrent Playback
The biggest “gotcha” with video_player is not testing multi-video scenarios. If your design has a scrolling list of auto-playing previews, you will hit the hardware decoder limit (often as low as 16-32 instances). The app will crash without a clear error. If your design requires this, media_kit is likely the safer choice from the start.
Final Verdict
There’s no universal winner. For most standard applications—social media feeds, e-learning content, simple video feeds—the official video_player is perfectly adequate and keeps your app lean. The moment your project steps into the realm of professional media handling, multi-platform deployment, or intensive multi-video layouts, the investment in media_kit’s size and complexity becomes justified. Assess your format requirements, concurrency needs, and target platforms first; the correct package choice will follow.
This blog is produced with the assistance of AI by a human editor. Learn more
Related Posts
Localizing Dynamic Content in Flutter: A Guide to Backend-Driven Translations
Many Flutter apps need to display content that changes based on user locale, but also comes from a backend (like Firebase). This post will explore best practices for fetching and integrating dynamic, localized content from a backend, ensuring a seamless user experience across different languages and regions without hardcoding translations.
Unraveling Type Mismatch Errors in Flutter: A Guide to 'X can't be assigned to Y' and '_InternalLinkedHashMap' Issues
Developers frequently encounter cryptic type mismatch errors like 'The argument type X can't be assigned to the parameter type Y' or '_InternalLinkedHashMap has no instance method 'cast''. This post will demystify these common Flutter/Dart type errors, explain their root causes (e.g., conflicting imports, dynamic typing pitfalls, JSON deserialization issues), and provide practical solutions to diagnose and fix them, improving code robustness and reducing debugging time.
Solving Flutter Web Memory Leaks: A Practical Guide to Identifying and Fixing Performance Issues
Flutter Web applications can suffer from increasing memory usage over time, leading to performance degradation. This post will delve into common causes of memory leaks in Flutter Web, provide practical debugging techniques using browser developer tools and Dart DevTools, and offer actionable strategies to identify and fix these issues for a smoother user experience.