A Flutter plugin for Android and iOS for playing back video on a Widget surface with full customization support. Build your own video player UI with complete control over the player, OR use our pre-built controls for instant integration!
- β Native video playback (ExoPlayer on Android, AVPlayer on iOS)
- β HLS streaming support
- β Subtitle support (SRT format)
- β Quality selection
- β Playback speed control
- β Pre-built controls - Use ready-made UI in seconds β‘
- β Full customization - Build your own controls if needed
- β External controller for programmatic control
- β Custom loading indicators
Add to your pubspec.yaml:
dependencies:
flutter_native_player: ^2.0.0Note: iOS requires 9.0 or higher and Android requires SDK 16 or higher
The easiest way to get started! Includes play/pause, seek, progress bar, quality selector, speed control, and subtitle selector.
import 'package:flutter_native_player/flutter_native_player.dart';
import 'package:flutter_native_player/model/player_resource.dart';
FlutterNativePlayer.withFullControls(
playerResource: PlayerResource(
videoUrl: 'https://example.com/video.m3u8',
playerSubtitleResources: subtitles, // Optional
),
width: double.infinity,
height: 250,
primaryColor: Colors.blue,
showQualitySelector: true,
showSpeedSelector: true,
showSubtitleSelector: true,
)Just play/pause and progress bar - perfect for most use cases.
FlutterNativePlayer.withBasicControls(
playerResource: PlayerResource(
videoUrl: 'https://example.com/video.m3u8',
),
width: double.infinity,
height: 250,
primaryColor: Colors.blue,
)Only a play/pause button - cleanest option.
FlutterNativePlayer.withMinimalControls(
playerResource: PlayerResource(
videoUrl: 'https://example.com/video.m3u8',
),
width: double.infinity,
height: 250,
)That's it! π You now have a fully functional video player with professional controls in just a few lines of code!
If you want just the video without any controls:
FlutterNativePlayer(
playerResource: PlayerResource(
videoUrl: 'https://example.com/video.m3u8',
),
playWhenReady: true,
width: double.infinity,
height: 250,
)If you need completely custom UI, build your own using the overlayBuilder:
import 'package:flutter_native_player/flutter_native_player.dart';
import 'package:flutter_native_player/flutter_native_player_controller.dart';
import 'package:flutter_native_player/method_manager/playback_state.dart';
import 'package:flutter_native_player/model/duration_state.dart';
FlutterNativePlayer(
playerResource: PlayerResource(videoUrl: videoUrl),
width: double.infinity,
height: 250,
overlayBuilder: (context, controller, playbackState, durationState) {
return Stack(
children: [
// Your custom UI here
Center(
child: IconButton(
icon: Icon(
playbackState == PlaybackState.play
? Icons.pause
: Icons.play_arrow,
),
onPressed: () => controller.playOrPause(),
),
),
],
);
},
)Control the player programmatically from anywhere in your app:
class MyPlayerScreen extends StatefulWidget {
@override
State<MyPlayerScreen> createState() => _MyPlayerScreenState();
}
class _MyPlayerScreenState extends State<MyPlayerScreen> {
final _controller = FlutterNativePlayerController();
@override
void initState() {
super.initState();
// Listen to playback state
_controller.playbackStateStream.listen((state) {
print('Playback state: $state');
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
// Video player (use any of the factory constructors)
FlutterNativePlayer.withBasicControls(
playerResource: PlayerResource(videoUrl: videoUrl),
controller: _controller,
playWhenReady: true,
width: double.infinity,
height: 250,
),
// External controls
Row(
children: [
ElevatedButton(
onPressed: () => _controller.play(),
child: Text('Play'),
),
ElevatedButton(
onPressed: () => _controller.pause(),
child: Text('Pause'),
),
ElevatedButton(
onPressed: () => _controller.seekForward(),
child: Text('+10s'),
),
],
),
],
);
}
width: double.infinity,
height: 250,
),
// External controls (anywhere in your widget tree)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.replay_10),
onPressed: () => _controller.seekBackward(),
),
IconButton(
icon: Icon(Icons.play_arrow),
onPressed: () => _controller.play(),
),
IconButton(
icon: Icon(Icons.pause),
onPressed: () => _controller.pause(),
),
IconButton(
icon: Icon(Icons.forward_10),
onPressed: () => _controller.seekForward(),
),
],
),
],
);
}
}The FlutterNativePlayerController provides complete player control:
controller.play(); // Start playback
controller.pause(); // Pause playback
controller.playOrPause(); // Toggle play/pause
controller.restart(); // Restart from beginning
controller.seekTo(Duration(seconds: 30)); // Seek to specific position
controller.seekForward(); // Forward 10 seconds (default)
controller.seekBackward(); // Backward 10 seconds (default)controller.setPlaybackSpeed(1.5); // 0.25x to 2.0x speed
double speed = controller.currentSpeed; // Get current speedList<QualityModel> qualities = controller.getAvailableQualities();
controller.changeQuality(qualities[0]); // Change quality
String url = controller.currentQualityUrl; // Current quality URLList<PlayerKidSubtitlesSource> subs = controller.getAvailableSubtitles();
controller.changeSubtitle(subs[0]); // Enable subtitle
controller.disableSubtitle(); // Turn off subtitles
PlayerKidSubtitlesSource? current = controller.currentSubtitle;// Listen to playback state changes
controller.playbackStateStream.listen((state) {
// PlaybackState.play, .pause, .loading, .finish
});
// Listen to position updates
controller.durationStateStream.listen((state) {
Duration progress = state.progress;
Duration total = state.total;
Duration buffered = state.buffered;
});
// Get current values
PlaybackState state = controller.playbackState;
DurationState? duration = controller.durationState;
Duration position = controller.currentPosition;
Duration total = controller.totalDuration;
bool loading = controller.isLoading;All factory constructors support customization:
FlutterNativePlayer.withFullControls(
playerResource: PlayerResource(videoUrl: url),
width: double.infinity,
height: 250,
primaryColor: Colors.purple, // Accent color for controls
controlsBackgroundColor: Colors.black54, // Background overlay color
autoHide: true, // Auto-hide controls
autoHideDuration: Duration(seconds: 3), // Hide after 3 seconds
showQualitySelector: true, // Show quality button
showSpeedSelector: true, // Show speed button
showSubtitleSelector: true, // Show subtitle button
backgroundColor: Colors.black, // Player background
)FlutterNativePlayer.withBasicControls(
playerResource: PlayerResource(videoUrl: url),
width: double.infinity,
height: 250,
loadingBuilder: (context, controller) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(color: Colors.blue),
SizedBox(height: 16),
Text('Loading...', style: TextStyle(color: Colors.white)),
],
),
);
},
)import 'package:flutter_native_player/model/player_subtitle_resource.dart';
FlutterNativePlayer.withFullControls(
playerResource: PlayerResource(
videoUrl: url,
playerSubtitleResources: [
PlayerSubtitleResource(
language: "English",
subtitleUrl: "https://example.com/subtitles_en.srt",
),
PlayerSubtitleResource(
language: "Spanish",
subtitleUrl: "https://example.com/subtitles_es.srt",
),
],
),
width: double.infinity,
height: 250,
showSubtitleSelector: true, // Show subtitle picker
showSubtitles: true, // Display subtitles
)If you have existing code using overlayBuilder, it continues to work! But consider migrating:
Before (200+ lines):
class MyPlayer extends StatefulWidget {
// Complex state management
// Custom control UI
// Stream listeners
// 200+ lines of code...
}
}After (11 lines):
FlutterNativePlayer.withFullControls(
playerResource: PlayerResource(videoUrl: url),
width: double.infinity,
height: 250,
)β¨ Result: 95% less code, same features!
// From zero to fully-functional player in 30 seconds
FlutterNativePlayer.withFullControls(
playerResource: PlayerResource(videoUrl: url),
width: double.infinity,
height: 250,
)- Pre-built controls - Ready to use out of the box
- Customizable - Change colors, hide features you don't need
- Fully custom - Build your own UI with
overlayBuilder
- Uses ExoPlayer on Android
- Uses AVPlayer on iOS
- Hardware-accelerated video decoding
- Optimized for battery life
- HLS streaming with automatic quality switching
- SRT subtitle support with multi-language
- Playback speed control (0.25x to 2.0x)
- Manual quality selection
- Programmatic control API
Check out the example folder for complete demonstrations:
simple_examples.dart- Quick start examples (recommended!)main.dart- Advanced customization examples
| Platform | Status |
|---|---|
| Android | β SDK 16+ |
| iOS | β iOS 9.0+ |
| Web | β Not supported |
| Desktop | β Not supported |
Contributions are welcome! Please read our contributing guidelines before submitting a PR.
This project is licensed under the MIT License - see the LICENSE file for details.
If you find this package helpful, please give it a β on GitHub! Duration total = controller.totalDuration; // Total duration Duration buffered = controller.bufferedDuration; // Buffered amount bool loading = controller.isLoading; // Loading state
### Streams (for reactive updates)
```dart
controller.playbackStateStream.listen((state) { });
controller.durationStateStream.listen((duration) { });
controller.loadingStream.listen((isLoading) { });
controller.downloadStateStream.listen((state) { });
controller.downloadProgressStream.listen((progress) { });
Future<bool> isPlaying = controller.isPlaying();
controller.showDevices(); // Show available devices
controller.release(); // Release player resources
controller.dispose(); // Dispose controller| Parameter | Type | Default | Description |
|---|---|---|---|
playerResource |
PlayerResource |
required | Video URL and subtitle resources |
width |
double |
required | Player width |
height |
double |
required | Player height |
playWhenReady |
bool |
true |
Auto-play when ready |
controller |
FlutterNativePlayerController? |
null |
External controller |
overlayBuilder |
PlayerOverlayBuilder? |
null |
Custom controls builder |
loadingBuilder |
PlayerLoadingBuilder? |
null |
Custom loading indicator |
showSubtitles |
bool |
true |
Show/hide subtitles |
backgroundColor |
Color |
Colors.black |
Background color |
- Android: Backed by ExoPlayer. See supported formats.
- iOS: Backed by AVPlayer. Formats vary by iOS version.
Version 2.0 removes the predefined UI controls in favor of full customization. If you were using the default controls, you'll need to build your own using the overlayBuilder parameter.
Before (v1.x):
FlutterNativePlayer(
playerResource: resource,
progressColors: PlayerProgressColors(...), // Removed
playWhenReady: true,
width: double.infinity,
height: 250,
)After (v2.0):
FlutterNativePlayer(
playerResource: resource,
playWhenReady: true,
width: double.infinity,
height: 250,
overlayBuilder: (context, controller, playbackState, durationState) {
return YourCustomControls(...);
},
)See the example project for complete implementations of custom controls.
MIT License
