multimedia_player 0.1.0
multimedia_player: ^0.1.0 copied to clipboard
A production-grade, cross-platform Flutter video player package supporting network URLs, local files, assets, RTSP, HLS, DASH with customizable controls and memory-efficient playback.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:multimedia_player/multimedia_player.dart';
void main() {
runApp(const MultimediaPlayerExampleApp());
}
class MultimediaPlayerExampleApp extends StatelessWidget {
const MultimediaPlayerExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Multimedia Player Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const ExampleHomePage(),
);
}
}
class ExampleHomePage extends StatelessWidget {
const ExampleHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Multimedia Player Examples'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildExampleCard(
context,
title: 'Basic Network Video',
description:
'Play a video from a network URL with default controls',
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const BasicNetworkVideoExample(),
),
),
),
_buildExampleCard(
context,
title: 'Custom Controls',
description: 'Video player with custom control UI',
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const CustomControlsExample()),
),
),
_buildExampleCard(
context,
title: 'Video List',
description: 'Multiple videos in a scrollable list with auto-pause',
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const VideoListExample()),
),
),
_buildExampleCard(
context,
title: 'HLS Stream',
description: 'Play HLS (.m3u8) adaptive streaming video',
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const HlsStreamExample()),
),
),
_buildExampleCard(
context,
title: 'Advanced Features',
description: 'Playback speed, volume control, and more',
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AdvancedFeaturesExample(),
),
),
),
],
),
);
}
Widget _buildExampleCard(
BuildContext context, {
required String title,
required String description,
required VoidCallback onTap,
}) {
return Card(
margin: const EdgeInsets.only(bottom: 16),
child: ListTile(
title: Text(
title,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(description),
),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: onTap,
),
);
}
}
// Basic Network Video Example
class BasicNetworkVideoExample extends StatefulWidget {
const BasicNetworkVideoExample({super.key});
@override
State<BasicNetworkVideoExample> createState() =>
_BasicNetworkVideoExampleState();
}
class _BasicNetworkVideoExampleState extends State<BasicNetworkVideoExample> {
late MultimediaVideoController _controller;
@override
void initState() {
super.initState();
// Sample video URL (Big Buck Bunny)
_controller = MultimediaVideoController(
source: VideoSource.network(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
),
config: const VideoPlayerConfig(autoPlay: true, looping: true),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Basic Network Video')),
body: Center(
child: AspectRatio(
aspectRatio: 16 / 9,
child: MultimediaVideoPlayer(controller: _controller),
),
),
);
}
}
// Custom Controls Example
class CustomControlsExample extends StatefulWidget {
const CustomControlsExample({super.key});
@override
State<CustomControlsExample> createState() => _CustomControlsExampleState();
}
class _CustomControlsExampleState extends State<CustomControlsExample> {
late MultimediaVideoController _controller;
@override
void initState() {
super.initState();
_controller = MultimediaVideoController(
source: VideoSource.network(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
),
config: const VideoPlayerConfig(autoPlay: false),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Custom Controls')),
body: Column(
children: [
Expanded(
child: MultimediaVideoPlayer(
controller: _controller,
controlsBuilder: (controller) => _buildCustomControls(controller),
),
),
_buildExternalControls(),
],
),
);
}
Widget _buildCustomControls(MultimediaVideoController controller) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Colors.black.withValues(alpha: 0.7)],
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: const Icon(Icons.replay_10, color: Colors.white),
onPressed: () => controller.seekBackward(),
),
IconButton(
icon: Icon(
controller.isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
size: 48,
),
onPressed: () => controller.togglePlayPause(),
),
IconButton(
icon: const Icon(Icons.forward_10, color: Colors.white),
onPressed: () => controller.seekForward(),
),
],
);
},
),
],
),
);
}
Widget _buildExternalControls() {
return Container(
padding: const EdgeInsets.all(16),
color: Colors.grey[200],
child: Column(
children: [
const Text(
'External Controls',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton.icon(
onPressed: () => _controller.play(),
icon: const Icon(Icons.play_arrow),
label: const Text('Play'),
),
ElevatedButton.icon(
onPressed: () => _controller.pause(),
icon: const Icon(Icons.pause),
label: const Text('Pause'),
),
ElevatedButton.icon(
onPressed: () => _controller.seekTo(Duration.zero),
icon: const Icon(Icons.replay),
label: const Text('Restart'),
),
],
),
],
),
);
}
}
// Video List Example
class VideoListExample extends StatelessWidget {
const VideoListExample({super.key});
static final List<String> _videoUrls = [
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4',
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Video List')),
body: ListView.builder(
itemCount: _videoUrls.length,
itemBuilder: (context, index) {
return VideoListItem(videoUrl: _videoUrls[index], index: index);
},
),
);
}
}
class VideoListItem extends StatefulWidget {
final String videoUrl;
final int index;
const VideoListItem({super.key, required this.videoUrl, required this.index});
@override
State<VideoListItem> createState() => _VideoListItemState();
}
class _VideoListItemState extends State<VideoListItem> {
late MultimediaVideoController _controller;
@override
void initState() {
super.initState();
_controller = MultimediaVideoController(
source: VideoSource.network(widget.videoUrl),
config: VideoPlayerConfig.forList(
autoPlay: false,
pauseOnInvisible: true,
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Video ${widget.index + 1}',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
AspectRatio(
aspectRatio: 16 / 9,
child: MultimediaVideoPlayer(
controller: _controller,
enableVisibilityTracking: true,
visibilityKey: ValueKey('video_${widget.index}'),
),
),
],
),
);
}
}
// HLS Stream Example
class HlsStreamExample extends StatefulWidget {
const HlsStreamExample({super.key});
@override
State<HlsStreamExample> createState() => _HlsStreamExampleState();
}
class _HlsStreamExampleState extends State<HlsStreamExample> {
late MultimediaVideoController _controller;
@override
void initState() {
super.initState();
// Sample HLS stream
_controller = MultimediaVideoController(
source: VideoSource.hls(
'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8',
),
config: const VideoPlayerConfig(
autoPlay: true,
showBufferingIndicator: true,
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('HLS Stream')),
body: Column(
children: [
Expanded(child: MultimediaVideoPlayer(controller: _controller)),
Container(
padding: const EdgeInsets.all(16),
color: Colors.grey[200],
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'HLS Adaptive Streaming',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
SizedBox(height: 8),
Text(
'This video uses HTTP Live Streaming (HLS) which automatically '
'adjusts quality based on network conditions.',
),
],
),
),
],
),
);
}
}
// Advanced Features Example
class AdvancedFeaturesExample extends StatefulWidget {
const AdvancedFeaturesExample({super.key});
@override
State<AdvancedFeaturesExample> createState() =>
_AdvancedFeaturesExampleState();
}
class _AdvancedFeaturesExampleState extends State<AdvancedFeaturesExample> {
late MultimediaVideoController _controller;
String _statusText = 'Initializing...';
@override
void initState() {
super.initState();
_controller = MultimediaVideoController(
source: VideoSource.network(
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
),
config: const VideoPlayerConfig(
autoPlay: false,
showPlaybackSpeed: true,
showVolumeControl: true,
),
);
// Setup event listeners
_controller.onPlay(() {
setState(() => _statusText = 'Playing');
});
_controller.onPause(() {
setState(() => _statusText = 'Paused');
});
_controller.onBuffering(() {
setState(() => _statusText = 'Buffering...');
});
_controller.onCompleted(() {
setState(() => _statusText = 'Completed');
});
_controller.onError((error) {
setState(() => _statusText = 'Error: $error');
});
_controller.onPositionChanged((position) {
// Position updates every 500ms
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Advanced Features')),
body: Column(
children: [
Expanded(child: MultimediaVideoPlayer(controller: _controller)),
Container(
padding: const EdgeInsets.all(16),
color: Colors.grey[200],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Status: $_statusText',
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
AnimatedBuilder(
animation: _controller,
builder: (context, child) {
final state = _controller.state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Position: ${FormatHelper.formatDuration(state.position)}',
),
Text(
'Duration: ${FormatHelper.formatDuration(state.duration)}',
),
Text('Speed: ${state.playbackSpeed}x'),
Text('Volume: ${(state.volume * 100).toInt()}%'),
Text(
'Buffered: ${FormatHelper.formatPercentage(state.bufferPercentage)}',
),
],
);
},
),
const SizedBox(height: 16),
Wrap(
spacing: 8,
children: [
ElevatedButton(
onPressed: () => _controller.setPlaybackSpeed(0.5),
child: const Text('0.5x'),
),
ElevatedButton(
onPressed: () => _controller.setPlaybackSpeed(1.0),
child: const Text('1.0x'),
),
ElevatedButton(
onPressed: () => _controller.setPlaybackSpeed(1.5),
child: const Text('1.5x'),
),
ElevatedButton(
onPressed: () => _controller.setPlaybackSpeed(2.0),
child: const Text('2.0x'),
),
],
),
],
),
),
],
),
);
}
}