flutter_ringtone_player_plus

pub package pub points CI License: MIT

Play system ringtones, alarms, notification sounds and custom audio on Android and iOS, with looping, volume and playback state.

The example app playing an alarm

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 RingtoneException instead of being silently ignored.
  • stop(), looping and volume also work on iOS.
  • fromFile needs an absolute path or a file:// URI.
  • IosSounds.voicemail and custom IosSound IDs 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.

License

MIT

Libraries

flutter_ringtone_player
The API of the flutter_ringtone_player package, so existing code only needs a new import.
flutter_ringtone_player_plus
Plays system ringtones, alarms, notification sounds and custom audio.
platform_interface
The platform interface of flutter_ringtone_player_plus, for platform implementations and for replacing the player with a fake in tests.