image

23 Jan 2026

9K

35K

Creating a Sliding Panel Widget for a Music Player in Flutter

A music player application often requires an intuitive and accessible UI for controlling playback without obstructing the main content. A sliding panel, common in many popular music apps, provides an elegant solution by offering a persistent player bar that can be expanded into a full-screen interface. This article will guide you through building such a sliding panel widget in Flutter, utilizing the popular sliding_up_panel package.

Why a Sliding Panel?

Sliding panels offer several advantages for music players:

  • Persistent Control: Users can always see and interact with the basic playback controls (play/pause, next/previous) regardless of the main screen content.
  • Space Efficiency: It maximizes screen real estate by allowing the full player UI to be hidden when not needed, revealing more of the app's primary content (e.g., song lists, albums).
  • Enhanced User Experience: A smooth sliding animation provides a modern and responsive feel, making the app more engaging.

Getting Started: The sliding_up_panel Package

While you could build a sliding panel from scratch using DraggableScrollableSheet or custom animations, the sliding_up_panel package simplifies this process significantly. It provides a highly customizable widget that handles the gestures, animations, and states required for a robust sliding panel.

Installation

First, add the sliding_up_panel dependency to your pubspec.yaml file:


dependencies:
  flutter:
    sdk: flutter
  sliding_up_panel: ^2.0.0 # Use the latest version

Then, run flutter pub get to fetch the package.

Implementing the Sliding Panel

The core of our music player will involve the SlidingUpPanel widget, which requires two main builders: panelBuilder for the content that slides up, and body for the main content behind the panel.

Basic Structure

Let's set up a basic Flutter application with a Scaffold and integrate SlidingUpPanel.


import 'package:flutter/material.dart';
import 'package:sliding_up_panel/sliding_up_panel.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Music Player',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MusicPlayerScreen(),
    );
  }
}

class MusicPlayerScreen extends StatefulWidget {
  const MusicPlayerScreen({super.key});

  @override
  State<MusicPlayerScreen> createState() => _MusicPlayerScreenState();
}

class _MusicPlayerScreenState extends State<MusicPlayerScreen> {
  final PanelController _panelController = PanelController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('My Music'),
      ),
      body: SlidingUpPanel(
        controller: _panelController,
        minHeight: 60, // Height of the collapsed panel
        maxHeight: MediaQuery.of(context).size.height * 0.8, // Max height for expanded panel
        panelBuilder: (sc) => _buildPanel(sc),
        body: _buildBody(),
        collapsed: _buildCollapsedPanel(), // Optional: a separate collapsed view
        borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
        parallaxEnabled: true,
        parallaxOffset: 0.1,
        panelSnapping: true,
      ),
    );
  }

  Widget _buildBody() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Text(
            'Welcome to your music library!',
            style: TextStyle(fontSize: 20),
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              // Example: Open the panel programmatically
              _panelController.open();
            },
            child: const Text('Open Player'),
          ),
          const SizedBox(height: 10),
          ElevatedButton(
            onPressed: () {
              // Example: Close the panel programmatically
              _panelController.close();
            },
            child: const Text('Close Player'),
          ),
        ],
      ),
    );
  }

  Widget _buildCollapsedPanel() {
    return Container(
      decoration: const BoxDecoration(
        color: Colors.blueGrey,
        borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
      ),
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Row(
        children: [
          const Icon(Icons.music_note, color: Colors.white),
          const SizedBox(width: 10),
          const Expanded(
            child: Text(
              'Now Playing: Song Title - Artist',
              style: TextStyle(color: Colors.white, fontSize: 16),
              overflow: TextOverflow.ellipsis,
            ),
          ),
          IconButton(
            icon: const Icon(Icons.play_arrow, color: Colors.white),
            onPressed: () {
              // Handle play/pause
            },
          ),
          IconButton(
            icon: const Icon(Icons.skip_next, color: Colors.white),
            onPressed: () {
              // Handle skip next
            },
          ),
        ],
      ),
    );
  }

  Widget _buildPanel(ScrollController sc) {
    return Container(
      decoration: const BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
      ),
      child: Column(
        children: [
          // Drag handle
          Container(
            width: 40,
            height: 5,
            margin: const EdgeInsets.symmetric(vertical: 8),
            decoration: BoxDecoration(
              color: Colors.grey[300],
              borderRadius: BorderRadius.circular(5),
            ),
          ),
          Expanded(
            child: ListView(
              controller: sc, // Important for scrollable content within the panel
              padding: EdgeInsets.zero,
              children: [
                Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    children: [
                      // Album Art
                      Container(
                        width: 200,
                        height: 200,
                        decoration: BoxDecoration(
                          color: Colors.blueGrey[100],
                          borderRadius: BorderRadius.circular(16),
                        ),
                        child: const Icon(Icons.album, size: 100, color: Colors.blueGrey),
                      ),
                      const SizedBox(height: 20),
                      const Text(
                        'Amazing Song Title',
                        style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                      ),
                      const Text(
                        'Artist Name - Album Name',
                        style: TextStyle(fontSize: 16, color: Colors.grey),
                      ),
                      const SizedBox(height: 20),
                      // Playback Controls
                      Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          IconButton(
                            icon: const Icon(Icons.skip_previous, size: 40),
                            onPressed: () {},
                          ),
                          IconButton(
                            icon: const Icon(Icons.play_circle_filled, size: 60),
                            onPressed: () {},
                          ),
                          IconButton(
                            icon: const Icon(Icons.skip_next, size: 40),
                            onPressed: () {},
                          ),
                        ],
                      ),
                      const SizedBox(height: 20),
                      // Placeholder for progress bar and volume controls
                      const LinearProgressIndicator(value: 0.5),
                      const SizedBox(height: 10),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: const [
                          Text('0:00'),
                          Text('3:45'),
                        ],
                      ),
                      const SizedBox(height: 20),
                      // Example for additional content
                      ListTile(
                        leading: const Icon(Icons.queue_music),
                        title: const Text('Up Next'),
                        onTap: () {},
                      ),
                      ListTile(
                        leading: const Icon(Icons.share),
                        title: const Text('Share Song'),
                        onTap: () {},
                      ),
                    ],
                  ),
                ),
                // Add more content here if needed
                const SizedBox(height: 100), // To make the list view scrollable
              ],
            ),
          ),
        ],
      ),
    );
  }
}

Key SlidingUpPanel Properties

  • controller: An instance of PanelController to programmatically open, close, or animate the panel.
  • minHeight: The height of the panel when it's collapsed.
  • maxHeight: The maximum height the panel can reach when expanded. This can be a fixed value or a percentage of screen height.
  • panelBuilder: A builder function that provides a ScrollController. This controller must be assigned to any scrollable widget within your panel to enable proper drag behavior.
  • collapsed: An optional widget that is displayed when the panel is fully collapsed. This allows for a dedicated UI for the minimized player. If omitted, the top part of panelBuilder content will be shown.
  • body: The widget displayed behind the panel, which typically represents the main content of your app (e.g., song list, albums).
  • borderRadius: Applies a border radius to the top corners of the panel, creating a floating card effect.
  • parallaxEnabled, parallaxOffset: Creates a parallax scrolling effect on the body content as the panel slides.
  • panelSnapping: When true, the panel will snap to its minHeight, maxHeight, and optionally a snappingControllers value.

Conclusion

By leveraging the sliding_up_panel package, you can effortlessly integrate a sleek and functional sliding music player panel into your Flutter application. This pattern enhances user experience by providing persistent controls and efficient use of screen space, making your music player both powerful and user-friendly. Experiment with different styles, animations, and additional controls to tailor it perfectly to your app's design.

Related Articles

May 14, 2026

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter Countdown timers are essential in many applic

May 11, 2026

Unleashing Dynamic UIs: Flutter's Animation Prowess

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects Flutter's declarative UI framework, combined with its powerful animation capabilit

May 11, 2026

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy A well-designed Product Detail Page (PDP) is