Flutter Media Playback — Audio/Video
Hello All Flutter Devs
In your daily development you might face the issue with the media playback how to play a remote video or audio any kind of playback issues,
Here we are going to see how to play a Video
and Audio
file from Internet, Assets and Local.
In this demo i am going to use three plugins they are
video_player
audioplayers
file_picker
So using this above three plugins we are going to build a demo project for MediaPlayBack
Let's Get Started,
Dependencies
Add the following dependencies in pubspec.yaml
video_player: ^0.10.2+1
file_picker: ^1.4.0
audioplayers: ^0.13.2
First we see how to play videos then we go for Audios
Video
Here how to goahead with video
Permission
Warning: The video player is not functional on iOS simulators. An iOS device must be used during development/testing.
Add the following entry to your Info.plist file, located in <project root>/ios/Runner/Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Add below permission in iOS for file_picker
<key>NSAppleMusicUsageDescription</key>
<string>Explain why your app uses music</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Explain why your app uses photo library</string>
Ensure the following permission is present in your Android Manifest file, located in <project root>/android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
Supported Formats
On iOS, the backing player is AVPlayer. The supported formats vary depending on the version of iOS, AVURLAsset class has audiovisualTypes that you can query for supported av formats.
On Android, the backing player is ExoPlayer, please refer here for list of supported formats.
Video From Internet
Declare a Variable in your code which a VideoPlayerController
, this one we are going to use it with VideoPlayer
VideoPlayerController _videoPlayerController = VideoPlayerController.network(
"https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_2mb.mp4",);
Now to Intialize the Controller call this in initState
@override
void initState() {
super.initState();
_videoPlayerController
..initialize().then((_) {
setState(() {});
});
_videoPlayerController..addListener(() {
setState(() {
});
});
}
Now to visualise the Player we need to add the UI code.
_videoPlayerController.value.initialized
? AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
child: VideoPlayer(_videoPlayerController),
)
: Container(),
In the above code what we did is, Once the player is initialized we we show the VideoPlayer
but we are using AspectRatio
that one is used to autoresize with the video height and width
By default VideoPlayer doesn't provide the controls for use we need to design and handle them by ourselfs
Center(
child: IconButton(
icon: Icon(
_videoPlayerController.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
onPressed: () {
_videoPlayerController.value.isPlaying ? _videoPlayerController.pause() : _videoPlayerController.play();
setState(() {
});
},
),
)
here in the above code _videoPlayerController.play()
will start the video playback _videoPlayerController.pause()
will pause the player
@override
void dispose() {
_videoPlayerController.removeListener(() {});
_videoPlayerController.dispose();
super.dispose();
}
Here is the Complete Code for the Video Playback from Internet
import 'package:video_player/video_player.dart';
import 'package:flutter/material.dart';
class VideoNetwork extends StatefulWidget {
@override
_VideoNetworkState createState() => _VideoNetworkState();
}
class _VideoNetworkState extends State<VideoNetwork> {
VideoPlayerController _videoPlayerController = VideoPlayerController.network(
"https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_2mb.mp4",);
@override
void initState() {
super.initState();
_videoPlayerController
..initialize().then((_) {
_videoPlayerController.setLooping(true);
setState(() {});
});
_videoPlayerController..addListener(() {
setState(() {
});
});
}
@override
void dispose() {
_videoPlayerController.removeListener(() {});
_videoPlayerController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Video From Network"),
),
body: Center(
child: Column(
children: <Widget>[
_videoPlayerController.value.initialized
? AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
child: VideoPlayer(_videoPlayerController),
)
: Container(),
Center(
child: IconButton(
icon: Icon(
_videoPlayerController.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
onPressed: () {
_videoPlayerController.value.isPlaying ? _videoPlayerController.pause() : _videoPlayerController.play();
setState(() {
});
},
),
)
],
),
),
);
}
}
Video From Assets
The only major changes between playing a Video from internt and Assets is the VideoPlayerController
part
VideoPlayerController _videoPlayerController = VideoPlayerController.asset(
"assets/video.mp4",);
For playing video from assets instead of VideoPlayerController.network
we use VideoPlayerController.asset
and remaining all works as above
Video From File
The only major changes between playing a Video from internt and Local is the VideoPlayerController
part
VideoPlayerController _videoPlayerController = VideoPlayerController.file(videoFile);
For playing video from Local instead of VideoPlayerController.network
we use VideoPlayerController.file
and remaining all works as above
So Inorder to get the file we will use file_picker
plugin
Pick a Video File
_pickVideoFileFrom(context) async {
File videoFile = await FilePicker.getFile(type: FileType.VIDEO);
if (videoFile == null) {
print("Video Picked is null");
}
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return VideoFile(
file: videoFile,
);
}));
}
Above code works to pick a file from the Local Storage and passed the file to the New Page where the video will be played
File videoFile = await FilePicker.getFile(type: FileType.VIDEO);
this code specified which file type should i get For Video we used FileType.VIDEO
and For Audio we will user FileType.AUDIO
final but important dispose
@override
void dispose() {
_videoPlayerController.removeListener(() {});
_videoPlayerController.dispose();
super.dispose();
}
Audio
Lets get started with Audio
AudioPlayer for Remote Files
Intialize Audio Player
AudioPlayer audioPlayer = AudioPlayer();
to start playing a remote file call
await audioPlayer.play(
"https://file-examples.com/wp-content/uploads/2017/11/file_example_MP3_2MG.mp3");
to pasuse or resume
// pause
audioPlayer.pause();
// resume
await audioPlayer.resume();
Note: This plugin doesn't provide any kind of UI for your player we need to build the UI by ourselfs
In order to listen the player state we need to listen fot the onPlayerStateChanged
audioPlayer.onPlayerStateChanged.listen((AudioPlayerState state) {
print("$state");
});
In order to listen the player duration updates we need to listen fot the onPlayerStateChanged
audioPlayer.onAudioPositionChanged.listen((Duration p) async {
print('Current position: $p');
});
To get the length of the song use audioPlayer.getDuration()
await audioPlayer.getDuration();
final but important dispose
@override
void dispose() async {
await audioPlayer.release();
await audioPlayer.dispose();
super.dispose();
}
Here is the complete AudioPlayer Code - Playing from Internet
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
class AudioNetwork extends StatefulWidget {
@override
_AudioNetworkState createState() => _AudioNetworkState();
}
class _AudioNetworkState extends State<AudioNetwork> {
AudioPlayer audioPlayer = AudioPlayer();
bool isPlaying = false;
bool isStarted = false;
Duration duration;
int time;
String timeLeft = "";
double progress = 0.0;
startPlaying() async {
if (!isStarted) {
await audioPlayer.play(
"https://file-examples.com/wp-content/uploads/2017/11/file_example_MP3_2MG.mp3");
isStarted = true;
} else
await audioPlayer.resume();
// time = await audioPlayer.getDuration();
}
getTimeLeft() {
if (duration == null) {
setState(() {
timeLeft = "Time Left 0s";
});
} else {
setState(() {
timeLeft = "Time Left ${duration.inSeconds}s";
});
}
}
getProgress() {
if (time == null || duration == null) {
setState(() {
progress = 0.0;
});
} else {
setState(() {
progress = time / (duration.inMilliseconds);
});
}
}
@override
void initState() {
super.initState();
audioPlayer.onAudioPositionChanged.listen((Duration p) async {
print('Current position: $p');
time = await audioPlayer.getDuration();
duration = p;
if (duration == null) {
timeLeft = "Time Left 0s/0s";
} else {
timeLeft = "Time Left ${duration.inSeconds}s/${time / 1000}s";
}
if (time == null || duration == null) {
progress = 0.0;
} else {
progress = (duration.inMilliseconds) / time;
}
print(progress);
setState(() {});
});
audioPlayer.onPlayerStateChanged.listen((AudioPlayerState state) {
print("$state");
if (state == AudioPlayerState.PLAYING) {
setState(() {
isPlaying = true;
});
} else {
if (mounted) {
setState(() {
isPlaying = false;
});
}
}
});
}
@override
void dispose() async {
await audioPlayer.release();
await audioPlayer.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Audio Player"),
),
body: Container(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
children: <Widget>[
LinearProgressIndicator(
value: progress,
),
SizedBox(
height: 16.0,
),
Text(timeLeft),
SizedBox(
height: 16.0,
),
Center(
child: IconButton(
icon: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
),
onPressed: () {
isPlaying ? audioPlayer.pause() : startPlaying();
setState(() {});
},
),
)
],
),
),
),
);
}
}
AudioPlayer for Local Files
The major change from playing remote files and local files is only about how we start playing, at the time we call play
we need to pass another paramer called isLoacal
to play a local file
First thing we need to pick a file and send it to the AudioPlayer Page where it gets played
_pickAudioFileFrom(context) async {
File audioFile = await FilePicker.getFile(type: FileType.AUDIO);
if (audioFile == null) {
print("Audio Picked is null");
}
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return AudioLocal(
file: audioFile,
);
}));
}
Playing an audio file
await audioPlayer.play(audioFile.path, isLocal: true);
final but important dispose
@override
void dispose() async {
await audioPlayer.release();
await audioPlayer.dispose();
super.dispose();
}
I will provide github link at the bottom
AudioPlayer for Assets
Playing an Audio File from assets is different from others mode
Intialize Audio Player
static AudioPlayer audioPlayer = AudioPlayer();
AudioCache audioPlayerCache = AudioCache(
fixedPlayer: audioPlayer
);
to start playing a remote file call
await audioPlayerCache.play("audio.mp3");
Remaining all are same as the other modes
final but important dispose
@override
void dispose() async {
await audioPlayer.release();
await audioPlayer.dispose();
super.dispose();
}
Here is the complete AudioPlayer Code - Playing from Assets
import 'package:audioplayers/audio_cache.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
class AudioAssets extends StatefulWidget {
@override
_AudioAssetsState createState() => _AudioAssetsState();
}
class _AudioAssetsState extends State<AudioAssets> {
static AudioPlayer audioPlayer = AudioPlayer();
AudioCache audioPlayerCache = AudioCache(
fixedPlayer: audioPlayer
);
bool isPlaying = false;
bool isStarted = false;
Duration duration;
int time;
String timeLeft = "";
double progress = 0.0;
startPlaying() async{
if(!isStarted) {
await audioPlayerCache.play(
"audio.mp3");
isStarted = true;
}
else await audioPlayer.resume();
// time = await audioPlayer.getDuration();
}
getTimeLeft() {
if(duration == null) {
setState(() {
timeLeft = "Time Left 0s";
});
} else {
setState(() {
timeLeft = "Time Left ${duration.inSeconds}s";
});
}
}
getProgress() {
if(time == null || duration == null) {
setState(() {
progress = 0.0;
});
} else {
setState(() {
progress = time / (duration.inMilliseconds);
});
}
}
@override
void initState() {
super.initState();
audioPlayer.onAudioPositionChanged.listen((Duration p) async {
print('Current position: $p');
time = await audioPlayer.getDuration();
duration = p;
if(duration == null) {
timeLeft = "Time Left 0s/0s";
} else {
timeLeft = "Time Left ${duration.inSeconds}s/${time/1000}s";
}
if(time == null || duration == null) {
progress = 0.0;
} else {
progress = (duration.inMilliseconds)/ time ;
}
print(progress);
setState(() {
});
});
audioPlayer.onPlayerStateChanged.listen((AudioPlayerState state) {
print("$state");
if (state == AudioPlayerState.PLAYING) {
setState(() {
isPlaying = true;
});
} else {
if(mounted) {
setState(() {
isPlaying = false;
});
}
}
});
}
@override
void dispose() async {
await audioPlayer.release();
await audioPlayer.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Audio Player"),
),
body: Container(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
children: <Widget>[
LinearProgressIndicator(
value: progress,
),
SizedBox(
height: 16.0,
),
Text(timeLeft),
SizedBox(
height: 16.0,
),
Center(
child: IconButton(
icon: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
),
onPressed: () {
isPlaying ? audioPlayer.pause() : startPlaying();
setState(() {});
},
),
)
],
),
),
),
);
}
}