Introduction
In this article, we will learn how to create a music player app in a flutter. Since flutter applications can run cross-platform using a single codebase, this application can also run on the iOS platform.
Prerequisite
- Having the latest version of Android Studio
- Having Installed Flutter and Dart in Android Studio
- Having Basic knowledge of Flutter Development
Let's start with Android Studio,
Create New Flutter Project In Android Studio
Open your Android Studio and create a new project. Edit the project name, android language, iOS language, and platform, and click Create button.
Add Required Dependancy In Pubspec.yaml
Add the following dependency in pubspec.yaml file, and please click on the pub get button.
http: ^0.13.5
audioplayers: ^3.0.1
HTTP Package -
HTTP is a network library that allows us to make HTTP requests to the web server and receive responses. HTTP package allows us to perform operations such as making GET and POST requests.
AudioPlayers Package -
This package allows us to play multiple audio files simultaneously. Using the audio player's package, we can play audio files from various sources, such as networks, file systems, and assets. We can use the AudioPlayer object to stop, play, pause, or complete a music file.
Open Main.dart
Go to main. dart and replace the existing code with the below code
import 'package:flutter/material.dart';
import 'Screens/HomePage.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
title: "Home Page",
home: HomePage(),
);
}
}
Explanation
main.dart is the entry point of a Flutter application. When the Flutter application is launched, then the main method is executed. We must return the MaterialApp here and set basic properties such as theme, title name, debug banner, etc. Here, We are calling the HomePage class.
We are going to use the below API URL and response data
API URL - https://storage.googleapis.com/uamp/catalog.json
API Response
{
"music": [{
"id": "wake_up_01",
"title": "Intro - The Way Of Waking Up (feat. Alan Watts)",
"album": "Wake Up",
"artist": "The Kyoto Connection",
"genre": "Electronic",
"source": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/01_-_Intro_-_The_Way_Of_Waking_Up_feat_Alan_Watts.mp3",
"image": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/art.jpg",
"trackNumber": 1,
"totalTrackCount": 13,
"duration": 90,
"site": "http://freemusicarchive.org/music/The_Kyoto_Connection/Wake_Up_1957/"
},
{
"id": "wake_up_02",
"title": "Geisha",
"album": "Wake Up",
"artist": "The Kyoto Connection",
"genre": "Electronic",
"source": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/02_-_Geisha.mp3",
"image": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/art.jpg",
"trackNumber": 2,
"totalTrackCount": 13,
"duration": 267,
"site": "http://freemusicarchive.org/music/The_Kyoto_Connection/Wake_Up_1957/"
},
{
"id": "wake_up_03",
"title": "Voyage I - Waterfall",
"album": "Wake Up",
"artist": "The Kyoto Connection",
"genre": "Electronic",
"source": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/03_-_Voyage_I_-_Waterfall.mp3",
"image": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/art.jpg",
"trackNumber": 3,
"totalTrackCount": 13,
"duration": 264,
"site": "http://freemusicarchive.org/music/The_Kyoto_Connection/Wake_Up_1957/"
},
{
"id": "wake_up_04",
"title": "The Music In You",
"album": "Wake Up",
"artist": "The Kyoto Connection",
"genre": "Electronic",
"source": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/04_-_The_Music_In_You.mp3",
"image": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/art.jpg",
"trackNumber": 4,
"totalTrackCount": 13,
"duration": 223,
"site": "http://freemusicarchive.org/music/The_Kyoto_Connection/Wake_Up_1957/"
},
{
"id": "wake_up_05",
"title": "The Calm Before The Storm",
"album": "Wake Up",
"artist": "The Kyoto Connection",
"genre": "Electronic",
"source": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/05_-_The_Calm_Before_The_Storm.mp3",
"image": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/art.jpg",
"trackNumber": 5,
"totalTrackCount": 13,
"duration": 229,
"site": "http://freemusicarchive.org/music/The_Kyoto_Connection/Wake_Up_1957/"
},
{
"id": "wake_up_06",
"title": "No Pain, No Gain",
"album": "Wake Up",
"artist": "The Kyoto Connection",
"genre": "Electronic",
"source": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/06_-_No_Pain_No_Gain.mp3",
"image": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/art.jpg",
"trackNumber": 6,
"totalTrackCount": 13,
"duration": 304,
"site": "http://freemusicarchive.org/music/The_Kyoto_Connection/Wake_Up_1957/"
},
{
"id": "wake_up_07",
"title": "Voyage II - Satori",
"album": "Wake Up",
"artist": "The Kyoto Connection",
"genre": "Electronic",
"source": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/07_-_Voyage_II_-_Satori.mp3",
"image": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/art.jpg",
"trackNumber": 7,
"totalTrackCount": 13,
"duration": 256,
"site": "http://freemusicarchive.org/music/The_Kyoto_Connection/Wake_Up_1957/"
},
{
"id": "wake_up_08",
"title": "Reveal the Magic",
"album": "Wake Up",
"artist": "The Kyoto Connection",
"genre": "Electronic",
"source": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/08_-_Reveal_the_Magic.mp3",
"image": "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/art.jpg",
"trackNumber": 8,
"totalTrackCount": 13,
"duration": 293,
"site": "http://freemusicarchive.org/music/The_Kyoto_Connection/Wake_Up_1957/"
}
]
}
Create a Model Class
Create a new directory named Model inside the lib folder. Now create a new file named MusicDataResponse.dart inside the Model folder and add the below code.
import 'package:flutter/material.dart';
class MusicDataResponse{
String? id;
String? title;
String? album;
String? artist;
String? genre;
String? source;
String? image;
int? trackNumber;
int? totalTrackCount;
int? duration;
String? site;
MusicDataResponse({
required this.id,
required this.title,
required this.album,
required this.artist,
required this.genre,
required this.source,
required this.image,
required this.trackNumber,
required this.totalTrackCount,
required this.duration,
required this.site});
factory MusicDataResponse.fromJson(Map<String,dynamic> json){
return MusicDataResponse(
id:json["id"],
title:json['title'],
album:json['album'],
artist:json['artist'],
genre:json['genre'],
source:json['source'],
image:json['image'],
trackNumber:json['trackNumber'],
totalTrackCount:json['totalTrackCount'],
duration:json['duration'],
site:json['site']);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['title'] = this.title;
data['album'] = this.album;
data['artist'] = this.artist;
data['genre'] = this.genre;
data['source'] = this.source;
data['image'] = this.image;
data['trackNumber'] = this.trackNumber;
data['totalTrackCount'] = this.totalTrackCount;
data['duration'] = this.duration;
data['site'] = this.site;
return data;
}
}
Explanation
Based on the API response data, we need to create a model class as above and define all the parameters we will use in our application.
Create an API Call Class
Create a new directory named Services inside the lib folder. Now create a new file named ApiService.dart inside the Services folder and add the below code.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:musicplayer/Model/MusicDataResponse.dart';
import 'dart:developer' as devLog;
class ApiService{
Future<List<MusicDataResponse>> getAllFetchMusicData()async {
const url = "https://storage.googleapis.com/uamp/catalog.json";
Uri uri = Uri.parse(url);
try {
final response = await http.get(uri);
if (response.statusCode == 200) {
final body=response.body;
final json=jsonDecode(body);
final result=json['music'] as List<dynamic>;
final musicList=result.map( (e) {
return MusicDataResponse.fromJson(e);
}).toList();
debugPrint(response.body.toString());
devLog.log(musicList.toString(),name: "MyMusicData");
return musicList;
} else {
return throw("Data fetch failed");
}
} catch (e) {
print(e);
return throw("Data fetch failed");
}
}
}
Explanation
Here we are going to make our API request and get the response. First, we declare the API URL and then make a request using the HTTP package. If we get a response status code is 200, it means API is executed successfully and maps JSON data to the model class; otherwise, we are throwing an error.
Set Path Of Images
Create a new directory named assets inside the lib folder. Now create a new directory named images inside the assets folder and paste any sample music image. This image is used as a placeholder. We need to set the path of this image in pubspec.yaml file.
Design Home Page
Create a new directory named Screens inside the lib folder. Now create a new file named HomePage.dart inside the Screens folder and add the below code.
import 'package:flutter/material.dart';
import 'package:musicplayer/Services/ApiService.dart';
import 'package:musicplayer/Model/MusicDataResponse.dart';
import 'MusicDetailPage.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<MusicDataResponse> musicList = [];
@override
void initState() {
super.initState();
fetchMusicData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Music App"),
),
body: customListCard());
}
Future<void> fetchMusicData() async {
final musiclist = await ApiService().getAllFetchMusicData();
setState(() {
musicList = musiclist;
});
}
Widget customListCard() {
return ListView.builder(
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MusicDetailPage(response: musicList[index]),
));
},
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 8),
child: Padding(
padding: const EdgeInsets.only(
left: 8, bottom: 8, right: 8, top: 4),
child: SizedBox(
child: FadeInImage.assetNetwork(
height: 60,
width: 60,
placeholder: "lib/assets/images/musicplaceholder.png",
image: musicList[index].image.toString(),
fit: BoxFit.fill),
),
),
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
musicList[index].title.toString(),
style: TextStyle(color: Colors.white, fontSize: 18),
),
SizedBox(
height: 8,
),
Text(
musicList[index].artist.toString(),
style: TextStyle(color: Colors.grey, fontSize: 12),
),
],
),
)
],
),
);
},
itemCount: musicList.length,
);
}
}
Explanation
Here we design our home screen and bind data that we get from API. Inside the initState() method, we call the fetchMusicData method to get API data, and once we get data, we initialize it into the list. Now we have to design a custom card to show an image, title, and subtitle inside the customListCard method and bind the data for each index using the ListView widget. In Flutter, to navigate from the home screen to the detail screen, we are using Navigator.
Home Screen Output
Design Music Detail Page
Inside the Screens directory, create a new file MusicDetailPage.dart, and add the below code.
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:musicplayer/Model/MusicDataResponse.dart';
class MusicDetailPage extends StatefulWidget {
MusicDetailPage({Key? key, required this.response}) : super(key: key);
final MusicDataResponse response;
@override
State<MusicDetailPage> createState() => _MusicDetailPageState();
}
class _MusicDetailPageState extends State<MusicDetailPage> {
final audioPlayer = AudioPlayer();
bool isPlaying = false;
Duration duration = Duration.zero;
Duration position = Duration.zero;
@override
void initState() {
super.initState();
setAudio();
audioPlayer.onPlayerStateChanged.listen((state) {
setState(() {
isPlaying = state == PlayerState.playing;
});
});
// listen to audio duration
audioPlayer.onDurationChanged.listen((newDuration) {
setState(() {
duration = newDuration;
});
});
// listen to audio position
audioPlayer.onPositionChanged.listen((newPosition) {
position = newPosition;
});
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
final url = widget.response.image.toString();
return Scaffold(
appBar: AppBar(
title: Text("Music Detail Page"),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.network(
height: MediaQuery.of(context).size.height/2.75,
url,
width: double.infinity,
fit: BoxFit.cover,
),
),
SizedBox(height: 32,),
Text(
widget.response.title.toString(),
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 4,),
Text(widget.response.artist.toString(),
style: TextStyle(
fontSize: 20,
),
),
Slider(
value: position.inSeconds.toDouble(),
min: 0,
activeColor: Colors.white,
max: duration.inSeconds.toDouble(),
onChanged: (value) async {
final position = Duration(seconds: value.toInt());
await audioPlayer.seek(position);
// optional :Play audio if was paused
await audioPlayer.resume();
}),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(formatTime(position)),
Text(formatTime(duration-position)),
],
),
),
CircleAvatar(
radius: 35,
child: IconButton(
onPressed: () async {
if (isPlaying) {
await audioPlayer.pause();
} else {
await audioPlayer.resume();
}
},
icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
iconSize: 50,
),
)
],
),
),
);
}
Future<void> setAudio()async {
// Repeat song when completed
audioPlayer.setReleaseMode(ReleaseMode.loop);
await audioPlayer.setSourceUrl(widget.response.source.toString());
}
String formatTime(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, "0");
final hours=twoDigits(duration.inHours);
final twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
final twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return [
if(duration.inHours>0)hours,
twoDigitMinutes,
twoDigitSeconds
].join(':');
}
}
Explanation
Here we are going to play audio. First, we create an AudioPlayer object and declare some other variables. Inside initState() method, we listen to whether the audio is playing or not, listen to the audio duration, and listen to the position of the audio. Inside the body, we design our detail screen and bind the data. We use the ClipRRect() widget to show an image and set its height using MediaQuery. Here we are using a Slider widget; when music is played, this slider moves from left to right. Lastly, we are using an IconButton to play and pause music.
Music Detail Screen Output
Launch your Application
Press the run button or press(shift+F10) and launch the application.
Output
Conclusion
This article shows how to create a music player using Web API in a flutter. You can read my other articles by clicking here. Please share your thoughts on this article if you have any suggestions or queries. Thanks for reading.
Happy learning, friends!