Flutter Functions

 

Building a Music Player App with Flutter: UI and Audio Playback

In today’s fast-paced world, music is an essential part of our lives. With the rise of mobile apps, music streaming has become more convenient than ever. Have you ever wondered how those music player apps are created? If you’re interested in app development and want to dive into the world of music streaming, you’re in the right place. In this tutorial, we’ll walk you through the process of building a music player app using Flutter, a popular UI framework developed by Google. We’ll cover both the UI design and audio playback aspects, so let’s get started!

Building a Music Player App with Flutter: UI and Audio Playback

1. Setting Up Your Flutter Project

Before we dive into the app’s design and functionality, make sure you have Flutter installed on your system. If not, you can follow the official installation guide on the Flutter website. Once you have Flutter up and running, create a new Flutter project using the following command:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bash
flutter create music_player_app
bash flutter create music_player_app
bash
flutter create music_player_app

Navigate to your project directory:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bash
cd music_player_app
bash cd music_player_app
bash
cd music_player_app

You’re all set to start building your music player app!

2. Designing the Music Player UI

A visually appealing user interface is crucial for any app’s success. Let’s design the UI of our music player app step by step.

2.1. Creating the App Layout

In Flutter, the UI is built using widgets. Open the lib/main.dart file and replace the default code with the following:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dart
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Music Player App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MusicPlayerScreen(),
);
}
}
class MusicPlayerScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Music Player'),
),
body: Center(
child: Text('Welcome to the Music Player App!'),
),
);
}
}
dart import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Music Player App', theme: ThemeData( primarySwatch: Colors.blue, ), home: MusicPlayerScreen(), ); } } class MusicPlayerScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Music Player'), ), body: Center( child: Text('Welcome to the Music Player App!'), ), ); } }
dart
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Music Player App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MusicPlayerScreen(),
    );
  }
}

class MusicPlayerScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Music Player'),
      ),
      body: Center(
        child: Text('Welcome to the Music Player App!'),
      ),
    );
  }
}

In this code, we’ve created the basic structure of our app with a title bar and a centered text widget. Now, let’s move on to designing the play screen.

2.2. Designing the Play Screen

The play screen will display album art, song details, and playback controls. Update the MusicPlayerScreen class as follows:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dart
class MusicPlayerScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Music Player'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset('assets/album_art.jpg'),
SizedBox(height: 20),
Text(
'Song Title',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
Text('Artist Name'),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.skip_previous),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.play_arrow),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.skip_next),
onPressed: () {},
),
],
),
],
),
);
}
}
dart class MusicPlayerScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Music Player'), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset('assets/album_art.jpg'), SizedBox(height: 20), Text( 'Song Title', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), Text('Artist Name'), SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: Icon(Icons.skip_previous), onPressed: () {}, ), IconButton( icon: Icon(Icons.play_arrow), onPressed: () {}, ), IconButton( icon: Icon(Icons.skip_next), onPressed: () {}, ), ], ), ], ), ); } }
dart
class MusicPlayerScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Music Player'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Image.asset('assets/album_art.jpg'),
          SizedBox(height: 20),
          Text(
            'Song Title',
            style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
          ),
          Text('Artist Name'),
          SizedBox(height: 20),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              IconButton(
                icon: Icon(Icons.skip_previous),
                onPressed: () {},
              ),
              IconButton(
                icon: Icon(Icons.play_arrow),
                onPressed: () {},
              ),
              IconButton(
                icon: Icon(Icons.skip_next),
                onPressed: () {},
              ),
            ],
          ),
        ],
      ),
    );
  }
}

In this code snippet, we’ve added placeholders for album art, song title, artist name, and playback controls. The Image.asset widget loads the album art from the assets folder.

2.3. Implementing the Playlist

To implement the playlist, we’ll create a separate screen that displays a list of songs. Add the following code to your main.dart:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dart
class PlaylistScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Playlist'),
),
body: ListView.builder(
itemCount: songs.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(songs[index].title),
subtitle: Text(songs[index].artist),
onTap: () {
// Navigate to the Play Screen with the selected song
},
);
},
),
);
}
}
dart class PlaylistScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Playlist'), ), body: ListView.builder( itemCount: songs.length, itemBuilder: (context, index) { return ListTile( title: Text(songs[index].title), subtitle: Text(songs[index].artist), onTap: () { // Navigate to the Play Screen with the selected song }, ); }, ), ); } }
dart
class PlaylistScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Playlist'),
      ),
      body: ListView.builder(
        itemCount: songs.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(songs[index].title),
            subtitle: Text(songs[index].artist),
            onTap: () {
              // Navigate to the Play Screen with the selected song
            },
          );
        },
      ),
    );
  }
}

Here, we’ve used a ListView.builder to display the list of songs. Each song is represented by a ListTile widget with its title and artist. You can replace songs with a list of song objects.

3. Integrating Audio Playback

Now that we have our UI in place, let’s move on to integrating audio playback functionality into our app.

3.1. Adding Audio Files to Your Project

Start by adding your audio files to the assets folder of your project. Make sure to update your pubspec.yaml file to include the audio files:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
yaml
flutter:
assets:
- assets/album_art.jpg
- assets/song1.mp3
- assets/song2.mp3
# Add more audio files as needed
yaml flutter: assets: - assets/album_art.jpg - assets/song1.mp3 - assets/song2.mp3 # Add more audio files as needed
yaml
flutter:
  assets:
    - assets/album_art.jpg
    - assets/song1.mp3
    - assets/song2.mp3
    # Add more audio files as needed

3.2. Setting Up Audio Playback Functionality

Flutter provides the audioplayers package for handling audio playback. Add this package to your pubspec.yaml file:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
yaml
dependencies:
flutter:
sdk: flutter
audioplayers: ^latest_version
yaml dependencies: flutter: sdk: flutter audioplayers: ^latest_version
yaml
dependencies:
  flutter:
    sdk: flutter
  audioplayers: ^latest_version

Replace latest_version with the actual version of the package.

In your MusicPlayerScreen class, import the necessary libraries and set up the audio player:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dart
import 'package:audioplayers/audioplayers.dart';
class MusicPlayerScreen extends StatelessWidget {
final AudioPlayer audioPlayer = AudioPlayer();
// ... Rest of the code ...
@override
Widget build(BuildContext context) {
// ... Rest of the build method ...
IconButton(
icon: Icon(Icons.play_arrow),
onPressed: () {
audioPlayer.play('assets/song1.mp3');
},
),
// ... Rest of the build method ...
}
}
dart import 'package:audioplayers/audioplayers.dart'; class MusicPlayerScreen extends StatelessWidget { final AudioPlayer audioPlayer = AudioPlayer(); // ... Rest of the code ... @override Widget build(BuildContext context) { // ... Rest of the build method ... IconButton( icon: Icon(Icons.play_arrow), onPressed: () { audioPlayer.play('assets/song1.mp3'); }, ), // ... Rest of the build method ... } }
dart
import 'package:audioplayers/audioplayers.dart';

class MusicPlayerScreen extends StatelessWidget {
  final AudioPlayer audioPlayer = AudioPlayer();

  // ... Rest of the code ...

  @override
  Widget build(BuildContext context) {
    // ... Rest of the build method ...

    IconButton(
      icon: Icon(Icons.play_arrow),
      onPressed: () {
        audioPlayer.play('assets/song1.mp3');
      },
    ),

    // ... Rest of the build method ...
  }
}

In this example, when the play button is pressed, the audio player will start playing the specified audio file.

3.3. Play, Pause, and Seek Controls

To add play, pause, and seek controls, modify your MusicPlayerScreen class as follows:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dart
class MusicPlayerScreen extends StatefulWidget {
@override
_MusicPlayerScreenState createState() => _MusicPlayerScreenState();
}
class _MusicPlayerScreenState extends State<MusicPlayerScreen> {
final AudioPlayer audioPlayer = AudioPlayer();
bool isPlaying = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Music Player'),
),
body: Column(
// ... Rest of the UI code ...
IconButton(
icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
onPressed: () {
if (isPlaying) {
audioPlayer.pause();
} else {
audioPlayer.play('assets/song1.mp3');
}
setState(() {
isPlaying = !isPlaying;
});
},
),
// ... Rest of the UI code ...
),
);
}
}
dart class MusicPlayerScreen extends StatefulWidget { @override _MusicPlayerScreenState createState() => _MusicPlayerScreenState(); } class _MusicPlayerScreenState extends State<MusicPlayerScreen> { final AudioPlayer audioPlayer = AudioPlayer(); bool isPlaying = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Music Player'), ), body: Column( // ... Rest of the UI code ... IconButton( icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow), onPressed: () { if (isPlaying) { audioPlayer.pause(); } else { audioPlayer.play('assets/song1.mp3'); } setState(() { isPlaying = !isPlaying; }); }, ), // ... Rest of the UI code ... ), ); } }
dart
class MusicPlayerScreen extends StatefulWidget {
  @override
  _MusicPlayerScreenState createState() => _MusicPlayerScreenState();
}

class _MusicPlayerScreenState extends State<MusicPlayerScreen> {
  final AudioPlayer audioPlayer = AudioPlayer();
  bool isPlaying = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Music Player'),
      ),
      body: Column(
        // ... Rest of the UI code ...

        IconButton(
          icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
          onPressed: () {
            if (isPlaying) {
              audioPlayer.pause();
            } else {
              audioPlayer.play('assets/song1.mp3');
            }
            setState(() {
              isPlaying = !isPlaying;
            });
          },
        ),

        // ... Rest of the UI code ...
      ),
    );
  }
}

Here, we’ve added a bool isPlaying variable to keep track of the playback state. The play button’s icon changes based on whether the audio is playing or paused.

4. Enhancing the User Experience

To create a polished music player app, let’s implement additional features that enhance the user experience.

4.1. Displaying Song Progress

Show the progress of the currently playing song using a progress indicator. Update your MusicPlayerScreen class:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dart
class _MusicPlayerScreenState extends State<MusicPlayerScreen> {
final AudioPlayer audioPlayer = AudioPlayer();
bool isPlaying = false;
double progress = 0.0;
@override
void initState() {
super.initState();
audioPlayer.onAudioPositionChanged.listen((Duration newPosition) {
setState(() {
progress = newPosition.inMilliseconds.toDouble();
});
});
}
@override
Widget build(BuildContext context) {
// ... Rest of the build method ...
Slider(
value: progress,
onChanged: (value) {
audioPlayer.seek(Duration(milliseconds: value.toInt()));
},
min: 0.0,
max: audioPlayer.duration.inMilliseconds.toDouble(),
),
// ... Rest of the build method ...
}
}
dart class _MusicPlayerScreenState extends State<MusicPlayerScreen> { final AudioPlayer audioPlayer = AudioPlayer(); bool isPlaying = false; double progress = 0.0; @override void initState() { super.initState(); audioPlayer.onAudioPositionChanged.listen((Duration newPosition) { setState(() { progress = newPosition.inMilliseconds.toDouble(); }); }); } @override Widget build(BuildContext context) { // ... Rest of the build method ... Slider( value: progress, onChanged: (value) { audioPlayer.seek(Duration(milliseconds: value.toInt())); }, min: 0.0, max: audioPlayer.duration.inMilliseconds.toDouble(), ), // ... Rest of the build method ... } }
dart
class _MusicPlayerScreenState extends State<MusicPlayerScreen> {
  final AudioPlayer audioPlayer = AudioPlayer();
  bool isPlaying = false;
  double progress = 0.0;

  @override
  void initState() {
    super.initState();
    audioPlayer.onAudioPositionChanged.listen((Duration newPosition) {
      setState(() {
        progress = newPosition.inMilliseconds.toDouble();
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    // ... Rest of the build method ...

    Slider(
      value: progress,
      onChanged: (value) {
        audioPlayer.seek(Duration(milliseconds: value.toInt()));
      },
      min: 0.0,
      max: audioPlayer.duration.inMilliseconds.toDouble(),
    ),

    // ... Rest of the build method ...
  }
}

In this code, the onAudioPositionChanged event listener updates the progress variable as the song plays. The Slider widget allows users to seek to a specific position in the song.

4.2. Implementing Shuffle and Repeat Functionality

Add shuffle and repeat buttons to your MusicPlayerScreen class:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dart
class _MusicPlayerScreenState extends State<MusicPlayerScreen> {
final AudioPlayer audioPlayer = AudioPlayer();
bool isPlaying = false;
bool isShuffling = false;
bool isRepeating = false;
// ... Rest of the code ...
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.shuffle, color: isShuffling ? Colors.blue : null),
onPressed: () {
setState(() {
isShuffling = !isShuffling;
});
},
),
IconButton(
icon: Icon(Icons.skip_previous),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.play_arrow),
onPressed: () {
// ... Rest of the play button logic ...
},
),
IconButton(
icon: Icon(Icons.skip_next),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.repeat, color: isRepeating ? Colors.blue : null),
onPressed: () {
setState(() {
isRepeating = !isRepeating;
});
},
),
],
),
// ... Rest of the code ...
}
dart class _MusicPlayerScreenState extends State<MusicPlayerScreen> { final AudioPlayer audioPlayer = AudioPlayer(); bool isPlaying = false; bool isShuffling = false; bool isRepeating = false; // ... Rest of the code ... Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: Icon(Icons.shuffle, color: isShuffling ? Colors.blue : null), onPressed: () { setState(() { isShuffling = !isShuffling; }); }, ), IconButton( icon: Icon(Icons.skip_previous), onPressed: () {}, ), IconButton( icon: Icon(Icons.play_arrow), onPressed: () { // ... Rest of the play button logic ... }, ), IconButton( icon: Icon(Icons.skip_next), onPressed: () {}, ), IconButton( icon: Icon(Icons.repeat, color: isRepeating ? Colors.blue : null), onPressed: () { setState(() { isRepeating = !isRepeating; }); }, ), ], ), // ... Rest of the code ... }
dart
class _MusicPlayerScreenState extends State<MusicPlayerScreen> {
  final AudioPlayer audioPlayer = AudioPlayer();
  bool isPlaying = false;
  bool isShuffling = false;
  bool isRepeating = false;

  // ... Rest of the code ...

  Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      IconButton(
        icon: Icon(Icons.shuffle, color: isShuffling ? Colors.blue : null),
        onPressed: () {
          setState(() {
            isShuffling = !isShuffling;
          });
        },
      ),
      IconButton(
        icon: Icon(Icons.skip_previous),
        onPressed: () {},
      ),
      IconButton(
        icon: Icon(Icons.play_arrow),
        onPressed: () {
          // ... Rest of the play button logic ...
        },
      ),
      IconButton(
        icon: Icon(Icons.skip_next),
        onPressed: () {},
      ),
      IconButton(
        icon: Icon(Icons.repeat, color: isRepeating ? Colors.blue : null),
        onPressed: () {
          setState(() {
            isRepeating = !isRepeating;
          });
        },
      ),
    ],
  ),

  // ... Rest of the code ...
}

The shuffle and repeat buttons change color based on their respective states. When shuffled, the app randomly selects the next song to play.

4.3. Handling Background Audio Playback

To allow audio playback even when the app is in the background, you need to implement background audio handling. Update your main.dart:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dart
void main() {
runApp(MyApp());
audioPlayer.startHeadlessService();
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// ... Rest of the code ...
);
}
}
dart void main() { runApp(MyApp()); audioPlayer.startHeadlessService(); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( // ... Rest of the code ... ); } }
dart
void main() {
  runApp(MyApp());
  audioPlayer.startHeadlessService();
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // ... Rest of the code ...
    );
  }
}

Adding audioPlayer.startHeadlessService() ensures that audio playback continues even if the app is minimized.

Conclusion

Congratulations! You’ve successfully built a music player app using Flutter, integrating a beautiful UI design and audio playback functionality. From creating a stunning play screen to handling audio playback and enhancing the user experience, you’ve covered the key aspects of building a music player app. This tutorial provides a solid foundation for further customization and feature expansion. Whether you’re a beginner or an experienced developer, Flutter opens the doors to crafting engaging and interactive apps that resonate with users’ interests and passions. Happy coding!

blank
Previously at
blank
Flag Argentina
Brazil
time icon
GMT-3
Full Stack Systems Analyst with a strong focus on Flutter development. Over 5 years of expertise in Flutter, creating mobile applications with a user-centric approach.