Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: CI

on: [push, pull_request]

jobs:
analyze-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
package:
- packages/flutter_radio_player
- packages/flutter_radio_player_platform_interface
- packages/flutter_radio_player_android
- packages/flutter_radio_player_ios
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: stable
- run: flutter pub get
working-directory: ${{ matrix.package }}
- run: flutter analyze --no-fatal-warnings
working-directory: ${{ matrix.package }}
- run: flutter test
working-directory: ${{ matrix.package }}
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# 4.0.0

* **BREAKING**: Full rewrite with federated plugin architecture (monorepo)
* **BREAKING**: Replaced `List<Map<String, String>>` with typed `RadioSource` model
* **BREAKING**: Renamed stream getters (`getPlaybackStream()` → `isPlayingStream`, etc.)
* **BREAKING**: Renamed `prevSource()` → `previousSource()`
* **BREAKING**: `getVolume()` now returns `Future<double>` (non-nullable)
* Added Pigeon for type-safe platform channels (replaces manual method/event channels)
* iOS: Replaced SwiftAudioEx with direct AVFoundation (no third-party deps)
* iOS: Raised minimum deployment target to 14.0
* iOS: Implemented `playOrPause()` (was missing)
* iOS: Added artwork URL support (was asset-only)
* iOS: Fixed volume event double-emission bug
* Added `dispose()` method on both platforms
* Added CI/CD via GitHub Actions
* Removed kotlinx-serialization dependency on Android

# 3.0.2

* Added foreground title when title was provided along with artist title
Expand Down
206 changes: 148 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,101 +2,183 @@

# Flutter Radio Player

![Pub Version](https://img.shields.io/pub/v/flutter_radio_player?style=plastic)
![Pub Likes](https://img.shields.io/pub/likes/flutter_radio_player)
![Pub Points](https://img.shields.io/pub/points/flutter_radio_player)
![Pub Popularity](https://img.shields.io/pub/popularity/flutter_radio_player)
[![Pub Version](https://img.shields.io/pub/v/flutter_radio_player)](https://pub.dev/packages/flutter_radio_player)
[![Pub Likes](https://img.shields.io/pub/likes/flutter_radio_player)](https://pub.dev/packages/flutter_radio_player)
[![Pub Points](https://img.shields.io/pub/points/flutter_radio_player)](https://pub.dev/packages/flutter_radio_player)
[![CI](https://github.com/Sithira/FlutterRadioPlayer/actions/workflows/ci.yml/badge.svg)](https://github.com/Sithira/FlutterRadioPlayer/actions/workflows/ci.yml)

**Flutter Radio Player** is the go-to plugin for playing a single streaming URL effortlessly. With support for background music playback right out of the box, it offers seamless integration with platform-native media controls. Whether it's lock screen media controls or deeper integrations like watchOS, CarPlay, WearOS, or Android Auto, Flutter Radio Player handles it all with no extra configuration needed.
A Flutter plugin for playing streaming radio URLs with background playback, lock screen controls, and platform-native media integrations including watchOS, WearOS, CarPlay, and Android Auto.

| | Android | iOS |
|-------------|---------|---------|
| **Support** | SDK 21+ | iOS 14+ |

## Features

- **Background Playback**: Plays audio in the background without any configuration.
- **Watch Integration**: Seamlessly integrates with WatchOS and WearOS for native watch control.
- **Automotive Systems**: Supports infotainment systems like Apple CarPlay and Android Auto.
- **Reactive by Default**: Automatically reacts to stream changes.
- **ICY/Metadata Extraction**: Extracts stream metadata if available.
- Background audio playback with no extra configuration
- Lock screen and notification media controls
- ICY/stream metadata extraction
- Multiple source queue with next/previous/jump-to navigation
- Volume control with stream updates
- watchOS, WearOS, CarPlay, and Android Auto integration

## Getting Started

### 1. Install the Player
### Installation

```bash
flutter pub add flutter_radio_player
```

### 2. Import the Library
### Usage

```dart
import 'package:flutter_radio_player/flutter_radio_player.dart';
```

### 3. Configure the Player
final player = FlutterRadioPlayer();

```dart
final _flutterRadioPlayerPlugin = FlutterRadioPlayer(); // Create an instance of the player
_flutterRadioPlayerPlugin.initialize(
// Initialize with sources
await player.initialize(
[
{"url": "https://s2-webradio.antenne.de/chillout?icy=https"},
{
"title": "SunFM - Sri Lanka",
"artwork": "images/sample-cover.jpg", // Image needs to be bundled with the app
"url": "https://radio.lotustechnologieslk.net:2020/stream/sunfmgarden?icy=https",
},
{"url": "http://stream.riverradio.com:8000/wcvofm.aac"}
const RadioSource(url: 'https://s2-webradio.antenne.de/chillout?icy=https'),
const RadioSource(
url: 'https://radio.lotustechnologieslk.net:2020/stream/sunfmgarden?icy=https',
title: 'SunFM - Sri Lanka',
artwork: 'images/sample-cover.jpg', // bundled asset
),
const RadioSource(url: 'http://stream.riverradio.com:8000/wcvofm.aac'),
],
true, // Auto play on load
playWhenReady: true,
);
```

Once configured, your player is ready to stream music.

### Manipulating the Player

You can control the player using the following methods:

| Method | Action |
|------------------------|------------------------------------------------------------|
| `play()` | Plays the audio from the current source |
| `pause()` | Pauses the audio |
| `playOrPause()` | Toggles play/pause |
| `changeVolume()` | Adjusts the volume |
| `getVolume()` | Retrieves the current volume |
| `nextSource()` | Skips to the next source in the list (if available) |
| `previousSource()` | Goes to the previous source |
| `jumpToSourceIndex()` | Jumps to a specific index in the sources list |
### Controlling Playback

### Available Streams

You can also listen to various streams:
```dart
await player.play();
await player.pause();
await player.playOrPause();
await player.nextSource();
await player.previousSource();
await player.jumpToSourceAtIndex(1);
await player.setVolume(0.8);
final volume = await player.getVolume();
await player.dispose();
```

| Stream | Returns | Description |
|-----------------------------------|-------------------------------------|------------------------------------------------------|
| `getIsPlayingStream()` | `Stream<bool>` | Emits playback status |
| `getNowPlayingStream()` | `Stream<NowPlayingDataChanged>` | Emits metadata such as track name |
| `getDeviceVolumeChangedStream()` | `Stream<double>` | Emits device audio level updates |
### Listening to Streams

## Platform Configuration
```dart
player.isPlayingStream.listen((bool isPlaying) {
print('Playing: $isPlaying');
});

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

To enable background playback, configure background capabilities in Xcode as shown below:
player.volumeStream.listen((VolumeInfo vol) {
print('Volume: ${vol.volume}, Muted: ${vol.isMuted}');
});
```

![Xcode Configuration](enabling-xcode-bg-service.png)
## Platform Setup

### Android

For Android, ensure the following permissions are added to your `AndroidManifest.xml`:
Add the following permissions to your app's `AndroidManifest.xml`:

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

> These permissions are already included in the library.
> These permissions are already declared by the plugin. You only need to add them if your app's manifest merger requires it.

### iOS

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

![Xcode Configuration](xcode_required_capabilities.png)

If your radio streams use plain HTTP, add the following to your `Info.plist`:

```xml
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
```

## API Reference

### Methods

| Method | Description |
|---------------------------|----------------------------------------|
| `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

| Stream | Type | Description |
|---------------------|-------------------------|----------------------------|
| `isPlayingStream` | `Stream<bool>` | Playback state changes |
| `nowPlayingStream` | `Stream<NowPlayingInfo>`| Track metadata updates |
| `volumeStream` | `Stream<VolumeInfo>` | Volume and mute changes |

### Models

```dart
const RadioSource({required String url, String? title, String? artwork});
const NowPlayingInfo({String? title});
const VolumeInfo({required double volume, required bool isMuted});
```

## Migration from v3

```diff
// Sources
- player.initialize([{"url": "...", "title": "..."}], true);
+ player.initialize([const RadioSource(url: '...', title: '...')], playWhenReady: true);

// Streams
- player.getPlaybackStream()
+ player.isPlayingStream

- player.getNowPlayingStream()
+ player.nowPlayingStream

- player.getDeviceVolumeChangedStream()
+ player.volumeStream

// Methods
- player.prevSource()
+ player.previousSource()

- player.setVolume(0.5) // unchanged
+ player.setVolume(0.5)

// New
+ await player.dispose();
+ await player.playOrPause(); // now works on iOS too
```

## Example

**Check out the [Flutter Radio Player Example](/example)** to see how to implement action methods and streams in your player.
![Example Player](example_player.png)

See the [example app](example/) for a complete implementation.

## Support the Plugin

Expand All @@ -106,5 +188,13 @@ If you find this plugin useful, show your support by:
- Leaving a like on Pub
- Showing some ♥️ and buying me a coffee via USDT-TR20 at this address: `TNuTkL1ZJGu2xntmtzHzSiH5YdVqUeAujr`

**Enjoy the plugin!**
Sithira ✌️
**Enjoy the plugin!**
Sithira ✌️

## Contributing

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

## License

[MIT](LICENSE)
9 changes: 0 additions & 9 deletions android/.gitignore

This file was deleted.

Binary file removed android/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 0 additions & 7 deletions android/gradle/wrapper/gradle-wrapper.properties

This file was deleted.

Loading