Skip to content

Repository files navigation

logo

Flutter Radio Player

Pub VersionPub LikesPub PointsCI

A Flutter plugin for playing streaming radio with background playback, lock screen controls, and platform-native media integrations.

Example Player

AndroidiOS
SupportSDK 21+iOS 14+

Features

  • Background audio playback with no extra configuration
  • Lock screen and notification media controls
  • ICY/Shoutcast metadata extraction
  • Multiple source queue with next/previous/jump-to navigation
  • Volume control with stream updates
  • Artwork support (asset paths and remote URLs)
  • watchOS, WearOS, CarPlay, and Android Auto integration
  • Swift Package Manager and CocoaPods support on iOS

Quick Start

Installation

flutter pub add flutter_radio_player

Minimal Example

import'package:flutter_radio_player/flutter_radio_player.dart';
final player =FlutterRadioPlayer();
await player.initialize([
constRadioSource(url:'https://example.com/stream'),
], playWhenReady:true);

Working with Sources

Define your radio stations using the RadioSource model:

const sources = [
RadioSource(url:'https://example.com/stream'),
RadioSource(
url:'https://example.com/jazz',
title:'Jazz FM',
artwork:'assets/jazz_cover.jpg', // bundled asset
),
RadioSource(
url:'https://example.com/rock',
title:'Rock Radio',
artwork:'https://example.com/rock_cover.png', // remote URL
),
];
await player.initialize(sources, playWhenReady:true);
FieldTypeDescription
urlStringStream URL (required)
titleString?Display name for lock screen
artworkString?Asset path or URL for album artwork

Working with Playback Controls

await player.play();
await player.pause();
await player.playOrPause(); // toggle// Navigate sourcesawait player.nextSource();
await player.previousSource();
await player.jumpToSourceAtIndex(2);
// Volume (0.0 to 1.0)await player.setVolume(0.8);
final volume =await player.getVolume();
// Clean up when doneawait player.dispose();

Working with Streams

Listen to real-time player state changes using streams:

Playback State

player.isPlayingStream.listen((bool isPlaying) {
print(isPlaying ?'Playing':'Paused');
});

Now Playing / ICY Metadata

player.nowPlayingStream.listen((NowPlayingInfo info) {
print('Now playing: ${info.title}');
});

The nowPlayingStream automatically extracts ICY metadata from Shoutcast/Icecast streams. If the stream provides metadata (e.g., artist and song title), it will appear here without any extra configuration.

Volume Changes

player.volumeStream.listen((VolumeInfo vol) {
print('Volume: ${vol.volume}, Muted: ${vol.isMuted}');
});

Full Example

A complete player widget with play/pause, skip controls, metadata display, and volume slider:

import'package:flutter/material.dart';
import'package:flutter_radio_player/flutter_radio_player.dart';
classRadioPlayerWidgetextendsStatefulWidget {
constRadioPlayerWidget({super.key});
@overrideState<RadioPlayerWidget> createState() =>_RadioPlayerWidgetState();
}
class_RadioPlayerWidgetStateextendsState<RadioPlayerWidget> {
final _player =FlutterRadioPlayer();
double _volume =0.5;
@overridevoidinitState() {
super.initState();
_player.initialize([
constRadioSource(
url:'https://s2-webradio.antenne.de/chillout?icy=https',
title:'Antenne Chillout',
),
constRadioSource(
url:'https://radio.lotustechnologieslk.net:2020/stream/sunfmgarden?icy=https',
title:'SunFM - Sri Lanka',
),
], playWhenReady:true);
}
@overridevoiddispose() {
_player.dispose();
super.dispose();
}
@overrideWidgetbuild(BuildContext context) {
returnColumn(
mainAxisAlignment:MainAxisAlignment.center,
children: [
// Now playing infoStreamBuilder<NowPlayingInfo>(
stream: _player.nowPlayingStream,
builder: (context, snapshot) {
final title = snapshot.data?.title ??'No track info';
returnText(title, style:Theme.of(context).textTheme.titleLarge);
},
),
constSizedBox(height:24),
// Transport controlsStreamBuilder<bool>(
stream: _player.isPlayingStream,
builder: (context, snapshot) {
final isPlaying = snapshot.data ??false;
returnRow(
mainAxisAlignment:MainAxisAlignment.center,
children: [
IconButton(
iconSize:36,
onPressed: _player.previousSource,
icon:constIcon(Icons.skip_previous_rounded),
),
IconButton(
iconSize:48,
onPressed: () => isPlaying ? _player.pause() : _player.play(),
icon:Icon(isPlaying ?Icons.pause_rounded :Icons.play_arrow_rounded),
),
IconButton(
iconSize:36,
onPressed: _player.nextSource,
icon:constIcon(Icons.skip_next_rounded),
),
],
);
},
),
constSizedBox(height:16),
// Volume sliderRow(
children: [
constIcon(Icons.volume_down_rounded),
Expanded(
child:Slider(
value: _volume,
onChanged: (value) {
setState(() => _volume = value);
_player.setVolume(value);
},
),
),
constIcon(Icons.volume_up_rounded),
],
),
],
);
}
}

See the example app for the full runnable project.

Platform Setup

Android

Add the following permissions to your AndroidManifest.xml:

<uses-permissionandroid:name="android.permission.INTERNET" />
<uses-permissionandroid:name="android.permission.FOREGROUND_SERVICE" />
<uses-permissionandroid:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

FOREGROUND_SERVICE and FOREGROUND_SERVICE_MEDIA_PLAYBACK are already declared by the plugin. INTERNET must be declared by the host app.

If your radio streams use plain HTTP (not HTTPS), opt in to cleartext traffic. Either add the attribute to your <application> tag:

<applicationandroid:usesCleartextTraffic="true"
...>

Or, preferred, whitelist only the streaming domains via a network security config (res/xml/network_security_config.xml):

<network-security-config>
<domain-configcleartextTrafficPermitted="true">
<domainincludeSubdomains="true">your-stream-host.example.com</domain>
</domain-config>
</network-security-config>

Then reference it from your <application> tag: android:networkSecurityConfig="@xml/network_security_config".

iOS

  1. Enable Audio, AirPlay, and Picture in Picture under your target's Signing & Capabilities > Background Modes in Xcode:

Xcode Configuration

  1. If your radio streams use plain HTTP, add the following to your Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
  1. Swift Package Manager is supported alongside CocoaPods (Flutter 3.24+). No additional configuration is needed.

API Reference

Methods

MethodDescription
initialize(sources)Set sources and optionally auto-play
play()Resume playback
pause()Pause playback
playOrPause()Toggle play/pause
setVolume(double)Set volume (0.0 to 1.0)
getVolume()Get current volume
nextSource()Skip to next source
previousSource()Skip to previous source
jumpToSourceAtIndex(i)Jump to source at index
dispose()Release player resources

Streams

StreamTypeDescription
isPlayingStreamStream<bool>Playback state changes
nowPlayingStreamStream<NowPlayingInfo>Track metadata updates
volumeStreamStream<VolumeInfo>Volume and mute changes

Models

ModelFields
RadioSourceurl (String), title (String?), artwork (String?)
NowPlayingInfotitle (String?)
VolumeInfovolume (double), isMuted (bool)

Architecture

This is a federated plugin split into four packages:

PackageDescription
flutter_radio_playerApp-facing API
flutter_radio_player_platform_interfaceShared interface and models
flutter_radio_player_androidAndroid implementation (Media3/ExoPlayer)
flutter_radio_player_iosiOS implementation (AVFoundation)

Platform communication uses Pigeon for type-safe code generation.

Migration from v3

See the Migration Guide for detailed upgrade instructions.

Support the Plugin

If you find this plugin useful:

  • Give it a star on GitHub
  • Leave a like on pub.dev
  • Buy me a coffee via USDT-TR20: TNuTkL1ZJGu2xntmtzHzSiH5YdVqUeAujr

Contributing

Contributions are welcome. Please open an issue first to discuss what you would like to change.

License

MIT

About

Flutter Radio Player, A Plugin to handle streaming audio without a hassle

Topics

Resources

Stars

156 stars

Watchers

12 watching

Forks

Releases

Packages

Used by

Contributors

Languages