flutter_ringtone_player_plus 1.0.0
flutter_ringtone_player_plus: ^1.0.0 copied to clipboard
Play system ringtones, alarms, notification sounds and custom audio on Android and iOS, with looping, volume and playback state.
flutter_ringtone_player_plus #
Play system ringtones, alarms, notification sounds and custom audio on Android and iOS, with looping, volume and playback state.
Features #
- The device's default alarm, notification and ringtone sounds
- Flutter assets and audio files from the device
- Looping, a volume scale that matches how loud sounds are perceived, and alarm, notification, ringtone or media audio
- Playback events and typed errors
- Audio focus on Android, audio session and interruption handling on iOS
- The API of
flutter_ringtone_player, so migrating takes one import change
Platform support #
| Android | iOS | |
|---|---|---|
| Minimum version | 7.0 (API 24) | 15.0 |
| Default sounds | The user's chosen sounds | Built-in sounds |
| Assets and files | ✓ | ✓ |
| Looping, volume and stop | ✓ | ✓ |
| Playback events | ✓ | ✓ |
| Swift Package Manager | – | ✓ |
macOS and web support is planned.
Usage #
import 'package:flutter_ringtone_player_plus/flutter_ringtone_player_plus.dart';
const player = RingtonePlayer();
await player.playAlarm();
await player.playNotification(volume: 0.5);
await player.stop();
Play your own sound from an asset or a file, with the same options:
await player.play(
const RingtoneSource.asset('assets/sounds/bell.mp3'),
options: const PlaybackOptions(
volume: 0.6,
looping: true,
usage: SoundUsage.alarm,
),
);
Listen for playback events, and catch errors when a sound cannot be played:
player.stateChanges.listen((state) {
// PlaybackState.playing, completed or stopped
});
try {
await player.play(RingtoneSource.file(path));
} on RingtoneException catch (error) {
// error.code is a RingtoneErrorCode, such as sourceNotFound
}
Platform notes #
Android. Default sounds are the ones the user picked in Settings. If one is not set, another sound available on the device is played. While a sound plays, the player holds audio focus: other apps lower their volume for notification sounds and pause for everything else.
iOS. Apps cannot access the user's ringtone or alarm, so default sounds are
built-in system sounds. Pick another one with iosSound:
await player.play(
const RingtoneSource.system(
RingtoneType.notification,
iosSound: IosSystemSound.glass,
),
);
Alarm and media sounds play even when the device is set to silent;
notification sounds and ringtones follow the silent switch. A phone call or
another audio interruption stops the sound. To keep a sound playing while your
app is in the background, add audio to UIBackgroundModes in Info.plist.
Testing #
Replace the player with a fake in your widget tests through the platform interface:
import 'package:flutter_ringtone_player_plus/flutter_ringtone_player_plus.dart';
import 'package:flutter_ringtone_player_plus/platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
class FakeRingtonePlayer extends RingtonePlayerPlatform
with MockPlatformInterfaceMixin {
final played = <RingtoneSource>[];
@override
Future<void> play(RingtoneSource source, PlaybackOptions options) async =>
played.add(source);
@override
Future<void> stop() async {}
@override
Stream<PlaybackState> get stateChanges => const Stream.empty();
}
void main() {
setUp(() => RingtonePlayerPlatform.instance = FakeRingtonePlayer());
}
Migrating from flutter_ringtone_player #
Replace the dependency and the import. The rest of your code keeps working:
dependencies:
flutter_ringtone_player_plus: ^1.0.0
// Before
import 'package:flutter_ringtone_player/flutter_ringtone_player.dart';
// After
import 'package:flutter_ringtone_player_plus/flutter_ringtone_player.dart';
What changes in behavior:
- Failures throw a
RingtoneExceptioninstead of being silently ignored. stop(), looping and volume also work on iOS.fromFileneeds an absolute path or afile://URI.IosSounds.voicemailand customIosSoundIDs play the default iOS sound for the call, because current iOS versions no longer ship those sounds.
For new code, the main API maps like this:
| flutter_ringtone_player | flutter_ringtone_player_plus |
|---|---|
FlutterRingtonePlayer().playAlarm() |
RingtonePlayer().playAlarm() |
play(fromAsset: 'a.mp3') |
play(RingtoneSource.asset('a.mp3')) |
play(fromFile: path) |
play(RingtoneSource.file(path)) |
play(android: AndroidSounds.notification, ios: IosSounds.glass) |
play(RingtoneSource.system(RingtoneType.notification, iosSound: IosSystemSound.glass)) |
volume: 0.5 (linear) |
PlaybackOptions(volume: 0.5) (perceptual) |
asAlarm: true |
PlaybackOptions(usage: SoundUsage.alarm) |
Why this package #
flutter_ringtone_player
is a popular package for playing system sounds, but its last release was in
February 2025. Fixes merged after that were never published, and issues such as
Android API 36 support are still open.
flutter_ringtone_player_plus is a maintained rewrite: Kotlin and Swift
instead of Java and Objective-C, a type-safe platform channel, tests on every
layer, and a migration path from the original API.
What's fixed #
Problems in flutter_ringtone_player 4.0.0+4 that this package fixes:
| Problem in the original | Upstream issue |
|---|---|
play(fromFile: ...) on its own throws "Please specify the sound source" |
#70 |
| Platform calls are not awaited, so native errors are silently lost | – |
| Plain strings are thrown instead of typed exceptions | – |
| No way to know whether a sound is playing or has finished | #74 |
| Volume uses a linear scale, so most values sound the same | #93 |
| Android: outdated build configuration, no API 36 support | #98 |
Android: the player can become null after an activity change (e.g. NFC reading) |
#100 |
| Android: an unknown sound type replies twice to the channel and can crash the app | – |
Android: file:// URIs never resolve |
– |
| Android: looping and volume are ignored below Android 9 | – |
| Android: playback is not released when the engine detaches | – |
| iOS: looping is not supported | #89 |
iOS: stop() does not stop system sounds, and sounds are capped at 30 seconds |
– |
iOS: fromFile paths are looked up as Flutter assets |
– |
| iOS: no Swift Package Manager support | – |
Credits #
Based on the ideas and API of flutter_ringtone_player by InWay.pro, released under the MIT license.
