quran_audio 0.1.0 copy "quran_audio: ^0.1.0" to clipboard
quran_audio: ^0.1.0 copied to clipboard

A Flutter package for Quran audio playback — logic only, no UI. Supports full-surah and per-ayah playback with system media notifications, offline downloads, and repeat modes.

Quran Audio #

pub package pub points likes Pub Downloads License: MIT

Web Windows macOS Android iOS

A Flutter package for Quran audio playback — logic only, no UI. It supports two independent playback systems (full-surah and per-ayah), system media notifications, offline downloads, and repeat modes — all through a single unified QuranAudio facade.

#

Table of Contents #

#

Getting started #

Android

The required permissions for audio playback (WAKE_LOCK, and FOREGROUND_SERVICE_MEDIA_PLAYOUT) are automatically added by the package. You don't need to manually edit your AndroidManifest.xml.

Additionally, to enable system-integrated audio controls (notification/lockscreen) using audio_service, your app's MainActivity must extend AudioServiceActivity:

Kotlin:

import com.ryanheise.audioservice.AudioServiceActivity

class MainActivity: AudioServiceActivity()

Java:

import com.ryanheise.audioservice.AudioServiceActivity;

public class MainActivity extends AudioServiceActivity {}

If you don't apply this change, the audio will still work locally, but system controls won't be available.

iOS

For background audio playback, you must add the following to your app's Info.plist:

<key>UIBackgroundModes</key>
<array>
  <string>audio</string>
</array>

macOS

macOS requires a network entitlement. Open macos/Runner/DebugProfile.entitlements and add:

<key>com.apple.security.network.client</key>
<true/>

Installation #

In the pubspec.yaml of your flutter project, add the following dependency:

dependencies:
  ...
  quran_audio: ^0.1.0

Import it:

import 'package:quran_audio/quran_audio.dart';

Initialize it:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Initialize the entire library in one step
  await QuranAudio.init();
  runApp(const MyApp());
}

💡 QuranAudio.init() accepts optional parameters:

await QuranAudio.init(
  enableSystemNotifications: true,   // false = no notifications (lighter, for web/tests)
  androidNotificationChannelId: 'com.myapp.quran',
  androidNotificationChannelName: 'Quran Playback',
  notificationIconSource: 'assets/images/logo.png', // custom icon (asset/url/file)
);

By default, QuranAudio.init() uses the library's bundled icon for the system media notification. To change it at runtime, see Notification Icon.

Notification Icon #

The system media notification (lock screen / Bluetooth / car audio) shows an icon. By default the library's bundled logo is used. You can set a custom icon from an asset, a network URL, or a local file:

// From an asset (most common)
await QuranAudio.setNotificationIconFromAsset('assets/images/my_logo.png');

// From a network URL
await QuranAudio.setNotificationIconFromUrl('https://example.com/logo.png');

// From a local file path
await QuranAudio.setNotificationIconFromFile('/path/to/logo.png');

// Auto-detect type (asset / url / file)
await QuranAudio.setNotificationIcon('assets/images/my_logo.png');

// Revert to the library's default icon
await QuranAudio.useDefaultNotificationIcon();

// Read the current icon URI
final Uri? icon = QuranAudio.notificationIcon;

The icon is refreshed live — if playback is active when you call these methods, the notification updates immediately.

Usage Example #

Play a Surah #

Play a full surah (single MP3 file per surah):

// Play Surah Al-Baqarah
QuranAudio.playSurah(2);

// Play Surah Al-Fatihah
QuranAudio.playSurah(1);

Play a Specific Ayah #

Play a specific ayah by surah number and ayah number:

// Play Ayat al-Kursi (2:255), continue to following ayahs
QuranAudio.playAyah(
  surah: 2,
  ayah: 255,
  singleAyah: false, // false = continue to next ayahs
);

// Play a single ayah then stop
QuranAudio.playAyah(
  surah: 36,
  ayah: 1,
  singleAyah: true,
);

Playback Controls #

Controls automatically execute on the currently active system — no need to know which is running.

QuranAudio.pause();               // Pause (active system)
QuranAudio.resume();              // Resume (active system)
QuranAudio.stop();                // Stop completely
QuranAudio.next();                // Next (surah or ayah)
QuranAudio.previous();            // Previous (surah or ayah)
QuranAudio.seek(Duration(minutes: 5));  // Seek to position
QuranAudio.togglePlayPause();     // Toggle play/pause
Complete Audio Example
class AudioPlayerScreen extends StatelessWidget {
  const AudioPlayerScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final c = QuranAudio.surahController;
    return Scaffold(
      appBar: AppBar(title: const Text('Quran Audio Player')),
      body: Column(
        children: [
          // Reactive now-playing display
          Obx(() {
            final surahNo = c.currentSurahNumber.value;
            final meta = QuranAudio.surah(surahNo);
            return Text('${meta.name} — ${meta.englishName}');
          }),

          // Control buttons
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              IconButton(
                onPressed: QuranAudio.previous,
                icon: const Icon(Icons.skip_previous),
              ),
              IconButton(
                onPressed: QuranAudio.togglePlayPause,
                icon: const Icon(Icons.play_arrow),
              ),
              IconButton(
                onPressed: QuranAudio.next,
                icon: const Icon(Icons.skip_next),
              ),
              IconButton(
                onPressed: QuranAudio.stop,
                icon: const Icon(Icons.stop),
              ),
            ],
          ),

          // Progress bar
          StreamBuilder<PositionData>(
            stream: QuranAudio.positionDataStream,
            builder: (context, snap) {
              final d = snap.data;
              return Slider(
                value: d?.position.inSeconds.toDouble() ?? 0,
                max: d?.duration.inSeconds.toDouble() ?? 1,
                onChanged: (v) => QuranAudio.seek(
                  Duration(seconds: v.toInt()),
                ),
              );
            },
          ),

          // Play buttons
          ElevatedButton(
            onPressed: () => QuranAudio.playSurah(1),
            child: const Text('Play Al-Fatihah'),
          ),
          ElevatedButton(
            onPressed: () => QuranAudio.playAyah(surah: 2, ayah: 255),
            child: const Text('Play Ayat al-Kursi'),
          ),
        ],
      ),
    );
  }
}

Reciters #

The library ships with two independent reciter lists (matching the original quran_library):

  • Surah reciters: 21 reciters (quranicaudio.com, mp3quran.net, tarteel.ai)
  • Ayah reciters: 12 reciters (cdn.islamic.network, everyayah.com)
// Change reciter by index
QuranAudio.setSurahReader(2);
QuranAudio.setAyahReader(1);

// Access the lists
final surahReaders = QuranAudio.surahReaders;
final ayahReaders = QuranAudio.ayahReaders;
final currentIndex = QuranAudio.surahReaderIndex;
Custom Reciters
// Override the default surah reciters list
SurahReaders.customReaders = [
  const ReaderInfo(
    index: 0,
    name: 'My Custom Reciter',
    readerNamePath: 'my_reader/',
    url: 'https://my-server.com/quran/',
  ),
];

// Override the default ayah reciters list
AyahReaders.customReaders = [
  const ReaderInfo(
    index: 0,
    name: 'My Custom Reciter',
    readerNamePath: 'my_reader',
    url: 'https://everyayah.com/data/',
  ),
];

Offline Downloads #

// Surah system (single MP3 file)
await QuranAudio.downloadSurah(2);                  // Download Al-Baqarah
final ready = await QuranAudio.isSurahDownloaded(2);
await QuranAudio.deleteSurahDownload(2);

// Ayah system (one MP3 per ayah)
await QuranAudio.downloadSurahAyahs(2);             // All ayahs of Al-Baqarah
await QuranAudio.downloadAyahRange(2, from: 1, to: 10);  // Range
final ready = await QuranAudio.isSurahAyahsDownloaded(2);
await QuranAudio.deleteSurahAyahDownloads(2);

// Cancel any ongoing download
QuranAudio.cancelDownload();

Repeat #

QuranAudio.repeatAyah();      // Repeat the current ayah
QuranAudio.repeatSurah();     // Repeat the current surah
QuranAudio.repeatAyahRange(   // Repeat a range of ayahs
  fromAyah: 1,
  toAyah: 10,
  times: 3,
);
QuranAudio.disableRepeat();   // Disable repeat

Reading State (GetX) #

⚠️ Important: Each Obx must read .value directly from a controller inside its builder. Do not wrap a whole widget in Obx if Rx values are read in a deeper build — GetX will throw an error.

final c = QuranAudio.surahController;

// ✅ Correct — .value is read directly inside Obx
Obx(() => Text(
  c.isPlaying.value ? 'Playing' : 'Paused',
));

Obx(() => Text('Surah: ${c.currentSurahNumber.value}'));
Progress bar & reactive card
// Progress bar via StreamBuilder (no Obx needed)
StreamBuilder<PositionData>(
  stream: QuranAudio.positionDataStream,
  builder: (context, snap) {
    final d = snap.data;
    return Slider(
      value: d?.position.inSeconds.toDouble() ?? 0,
      max: d?.duration.inSeconds.toDouble() ?? 1,
      onChanged: (v) => QuranAudio.seek(Duration(seconds: v.toInt())),
    );
  },
);

// A reactive now-playing card
class NowPlayingCard extends StatelessWidget {
  Widget build(BuildContext context) {
    final surahC = QuranAudio.surahController;
    final ayahC = QuranAudio.ayahController;
    return Obx(() {
      final surahNo = surahC.currentSurahNumber.value;
      final ayahNo = ayahC.currentAyahInSurah.value;
      final playing = surahC.isPlaying.value || ayahC.isPlaying.value;
      final meta = QuranAudio.surah(surahNo);
      return Card(
        child: ListTile(
          title: Text(meta.name),
          subtitle: Text(playing ? 'Playing' : 'Paused'),
          trailing: Text('$ayahNo / ${meta.ayahCount}'),
        ),
      );
    });
  }
}

Metadata Helpers #

Instead of bundling the full Quran file (745KB), the library uses a tiny (~3KB) JSON holding only surah numbers, names, and ayah counts. Unique ayah numbers (UQ 1..6236) are computed cumulatively — no need to store them.

QuranAudio.ayahCountOf(2);          // → 286
QuranAudio.uqNumberOf(2, 255);      // → 262
QuranAudio.surah(2).name;           // → "Surat Al-Baqarah"
QuranAudio.allSurahs;               // → List<SurahMeta> (114 entries)

// Validation
QuranAudio.metadata.isValidSurah(2);           // → true
QuranAudio.metadata.isValidAyah(2, 255);       // → true

How it Works #

The library maintains two independent playback systems, each with its own reciters, URLs, and mechanisms:

System Description Reciters Sources
Surah (SurahAudioController) One MP3 per full surah 21 quranicaudio.com, mp3quran.net, tarteel.ai
Ayah (AyahAudioController) One MP3 per ayah 12 cdn.islamic.network, everyayah.com

They share a single AudioPlayer via a coordination layer (AudioEngine). When one system takes control, the other is automatically stopped and its state is reset.

  • Auto-advance: Ayahs play sequentially (playlist + windowing); surahs auto-advance to the next (1→114) on completion.
  • Smart controls: pause()/resume()/next()/previous() execute on whichever system is currently active.
  • Direct access: For advanced use, the controllers are exposed via QuranAudio.surahController and QuranAudio.ayahController.

Sources #

  • Quran metadata (surah names & ayah counts): derived from King Fahd Glorious Quran Printing Complex data
  • Audio sources: quranicaudio.com, mp3quran.net, tarteel.ai, cdn.islamic.network, everyayah.com

License #

MIT for code. Read more about the license here.

1
likes
160
points
0
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter package for Quran audio playback — logic only, no UI. Supports full-surah and per-ayah playback with system media notifications, offline downloads, and repeat modes.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

audio_service, dio, flutter, get, get_storage, just_audio, just_audio_media_kit, path, path_provider, rxdart

More

Packages that depend on quran_audio