How to Build a Music Player in Flutter
A music player may be added to your Flutter app in this tutorial. To play music from external storage, to play from an assets file, or to use a URL to play music (internet). The ability to adjust the volume of the song.
Introduction
Emotions may be expressed via music, which is a universal language. In the modern world, it’s clear that music applications are a must-have. Listening to their favorite songs on a variety of music applications is a popular way for people to cope with stress or improve their creative abilities. A developer’s job isn’t complete until he or she creates useful applications for themselves. Since you’re a fan of music, I decided to create a customized music app of my own. Here, I’ll show you how to create a simple music app in a Flutter.
Packages Used:
Our external storage will be accessed using Flutter audio query (eg. mobile phone, memory card, etc).
Flutter_audio_query: ^0.3.5+6
https://pub.dev/packages/flutter_audio_query
We may utilize the audio manager package to integrate features like play, pause, seek, inc. or dec. volume in our program.
audio_manager: ^0.8.2
https://pub.dev/packages/audio_manager
Setting Up the Project:
import the packages
import 'package:flutter_audio_query/flutter_audio_query.dart';
import 'package:audio_manager/audio_manager.dart';
Modify your AndroidManifest.xml
<application
...
android:usesCleartextTraffic="true"
...
>
Modify your build.gradle file.
defaultConfig {
minSdkVersion 23
}
Using the internet and other resources to play music:
Creating an audio manager instance
var audioManagerInstance = AudioManager.instance;
Playing music using the start method AudioManager has a start() function that we may use to start playing the music. Input includes a URL as well as a title, description, and a cover image, as well as an optional auto-complete field.
onTap: () {
audioManagerInstance
.start("song URL", "song title",
desc: "description",
auto: true,
cover: "cover URL")
.then((err) {
print(err);
});
},
To listen to music from an assets file, just modify the song’s URL to the location of the assets file.
onTap: () {
audioManagerInstance
.start("assets/music.mp3"song title",
desc: "description",
auto: true,
cover: "assets/cover.png")
.then((err) {
print(err);
});
},
Our external storage is being used to download music:
FlutterAudioQuery produces a future, thus we’ll use a FutureBuilder to get the music files from external storage. For example, we can use the getSongs function to get all the songs from a certain artist or album. Only the getSongs function will be used to keep the logic basic and clean. Every single one of them is yours for the taking.
FutureBuilder(
future: FlutterAudioQuery()
.getSongs(sortType: SongSortType.RECENT_YEAR),
builder: (context, snapshot) {
List<SongInfo> songInfo = snapshot.data;
if (snapshot.hasData) return SongWidget(songList: songInfo);
return Container(
height: MediaQuery.of(context).size.height * 0.4,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
SizedBox(
width: 20,
),
Text(
"Loading....",
style: TextStyle(fontWeight: FontWeight.bold),
)
],
),
),
);
},
)
SongWidget:
External memory may only be used in the song’s path is known. The filePath field of the SongInfo class allows us to get the location of the music file.
onTap: () {
audioManagerInstance
.start("file://${song.filePath}", song.title,
desc: song.displayName,
auto: true,
cover: song.albumArtwork)
.then((err) {
print(err);
});
},
Setting Up the audio:
This is the most crucial portion since it controls a variety of audio occurrences.
void setupAudio() {
audioManagerInstance.onEvents((events, args) {
switch (events) {
case AudioManagerEvents.start:
_slider = 0;
break;
case AudioManagerEvents.seekComplete:
_slider = audioManagerInstance.position.inMilliseconds /
audioManagerInstance.duration.inMilliseconds;
setState(() {});
break;
case AudioManagerEvents.playstatus:
isPlaying = audioManagerInstance.isPlaying;
setState(() {});
break;
case AudioManagerEvents.timeupdate:
_slider = audioManagerInstance.position.inMilliseconds /
audioManagerInstance.duration.inMilliseconds;
audioManagerInstance.updateLrc(args["position"].toString());
setState(() {});
break;
case AudioManagerEvents.ended:
audioManagerInstance.next();
setState(() {});
break;
default:
break;
}
});
}
Initializing setupAudio
void initState() {
super.initState();
setupAudio();
}
Creating a control panel:
This panel contains a play-pause button, previous button, next button, and a songProgress Slider.
Widget bottomPanel() {
return Column(children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: songProgress(context),
),
Container(
padding: EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
CircleAvatar(
child: Center(
child: IconButton(
icon: Icon(
Icons.skip_previous,
color: Colors.white,
),
onPressed: () => audioManagerInstance.previous()),
),
backgroundColor: Colors.cyan.withOpacity(0.3),
),
CircleAvatar(
radius: 30,
child: Center(
child: IconButton(
onPressed: () async {
if(audioManagerInstance.isPlaying)
audioManagerInstance.toPause();
audioManagerInstance.playOrPause();
},
padding: const EdgeInsets.all(0.0),
icon: Icon(
audioManagerInstance.isPlaying
? Icons.pause: Icons.play_arrow,
color: Colors.white,
),
),
),
),
CircleAvatar(
backgroundColor: Colors.cyan.withOpacity(0.3),
child: Center(
child: IconButton(
icon: Icon(
Icons.skip_next,
color: Colors.white,
),
onPressed: () => audioManagerInstance.next()),
),
),
],
),
),
]);
}
Duration of a song:
Using this function, we may format the song’s length in milliseconds into this format: 000:00. Format, in this case, is a string equal to 00:00. The song’s runtime is used to calculate the duration. Otherwise, it returns the provided duration formatted as [insert format here].
String _formatDuration(Duration d) {
if (d == null) return "--:--";
int minute = d.inMinutes;
int second = (d.inSeconds > 60) ? (d.inSeconds % 60) : d.inSeconds;
String format = ((minute < 10) ? "0$minute" : "$minute") +
":" +
((second < 10) ? "0$second" : "$second");
return format;
}
SongProgress:
Widget songProgress(BuildContext context) {
var style = TextStyle(color: Colors.black);
return Row(
children: <Widget>[
Text(
_formatDuration(audioManagerInstance.position),
style: style,
),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
trackHeight: 2,
thumbColor: Colors.blueAccent,
overlayColor: Colors.blue,
thumbShape: RoundSliderThumbShape(
disabledThumbRadius: 5,
enabledThumbRadius: 5,
),
overlayShape: RoundSliderOverlayShape(
overlayRadius: 10,
),
activeTrackColor: Colors.blueAccent,
inactiveTrackColor: Colors.grey,
),
child: Slider(
value: _slider ?? 0,
onChanged: (value) {
setState(() {
_slider = value;
});
},
onChangeEnd: (value) {
if (audioManagerInstance.duration != null) {
Duration msec = Duration(
milliseconds:
(audioManagerInstance.duration.inMilliseconds *
value)
.round());
audioManagerInstance.seekTo(msec);
}
},
)),
),
),
Text(
_formatDuration(audioManagerInstance.duration),
style: style,
),
],
);
}
Conclusion
After all these steps, we have got the following output: