The Frustration That Started Everything
I was looking for a Quran app that I could genuinely recommend to my family. Simple requirement. Impossible to fulfil, apparently.
The first one I tried: three full-screen ads before I could open a Surah. The second: paywalled translations, $4.99 per language. The third: beautiful UI, but only supported English and Arabic. My mother reads Quran in Tamil. My colleague reads it in Malay. My friend in Germany uses the German translation. None of these apps served them.
For a book that is read by over a billion people daily, in hundreds of languages, in countries ranging from Norway to Nigeria to Indonesia — the state of available apps was genuinely embarrassing.
So I decided to build one. Not to make money. Not to build a portfolio project. To make something that actually works for everyone, in every language, for free.
That was 2021. What followed was two years of late nights, unexpected engineering challenges, and a humbling education in what it actually takes to build software that works globally.
Why Flutter Was the Only Real Choice
The first technical decision was the stack. The app needed to run on iOS, Android, macOS, Windows, and the web — not eventually, but from the start. My users weren't all on mobile. Students often use desktop apps for longer reading sessions. Some users access the web version in countries where storage-limited phones can't accommodate a full app install.
Native per-platform development was out — I'm one developer, not five. React Native was an option, but my existing expertise was in Flutter, and the rendering performance for Arabic text (which I'll get to) was substantially better in Flutter's Impeller engine. Electron for desktop felt wrong for what is essentially a text reader.
Flutter gave me a single codebase that ran everywhere. It also gave me the pdf package, which turned out to be crucial for the most-requested feature I didn't plan to build.
The Translation Challenge: 90+ Languages, 470+ Editions
This is where the project got technically interesting.
Supporting one Quran translation is easy — download a JSON file, display it. Supporting 470+ translations in 90+ languages is a data engineering problem as much as a software problem.
The Quran has 114 Surahs and 6,236 Ayahs. Multiply that by 470 translations, and you're talking about roughly 2.9 million individual text entries that need to be stored, indexed, retrieved, and rendered — in real time, without lag, on a mid-range Android phone.
My first approach was naive: fetch translations from an API on demand. This worked on fast connections. On a 2G connection in rural Bangladesh — one of my highest-traffic regions — it was unusable. Page loads took 8-12 seconds. Users left.
The second approach: bundle all translations in the app. This made the app 340MB. It got rejected from both app stores for exceeding size limits.
The third approach — the one that actually works — is what I call the tiered data strategy:
- Tier 1 (bundled): The Arabic text and the user's most recently selected translation are bundled in the app binary. Cold open loads in under 300ms.
- Tier 2 (downloaded on demand): Any translation the user switches to is downloaded once, then cached indefinitely in Hive on-device.
- Tier 3 (streamed): For the web version, translations stream in chunks. The first Ayah of any Surah renders before the full translation is loaded.
The result: a 12MB app that behaves like it has everything installed, because it fetches exactly what each user actually uses.
Arabic Text Rendering: The Problem Nobody Talks About
Arabic is a right-to-left language with complex ligature rules. Letters change shape depending on their position in a word. The Quranic script (Uthmani) uses diacritical marks (Tashkeel) that affect vertical spacing. Several common Arabic fonts render these incorrectly on certain Android versions.
I spent three weeks on this problem alone.
The solution that worked: I bundle a specific version of the Hafs Uthmani font (a modified KFGQPC build) directly in the app assets, and force Flutter's text engine to use it for all Arabic rendering rather than relying on the system font. This adds 2.1MB to the binary size but guarantees pixel-perfect Arabic text on every device.
// Forcing the custom Arabic font
TextStyle quranAyahStyle(double fontSize) => TextStyle(
fontFamily: 'HafsUthmani',
fontSize: fontSize,
height: 2.2, // Critical for Tashkeel to not overlap
letterSpacing: 0,
);
// In pubspec.yaml:
fonts:
- family: HafsUthmani
fonts:
- asset: assets/fonts/hafs_uthmani.ttfThe height: 2.2 value took embarrassingly long to get right. Too low and the diacritical marks overlap with the line above. Too high and the text feels sparse. 2.2 is the sweet spot for the Uthmani script at typical reading font sizes.
Offline-First Architecture with Hive
From day one, I designed the app to work without internet. This was non-negotiable. My users include people in regions with intermittent connectivity, people on flights, and people who simply don't want an app that breaks without a signal.
The architecture is simple: every piece of data the app uses has two sources — a remote source and a local Hive box. The repository layer always checks local first:
class QuranRepositoryImpl implements QuranRepository {
final QuranApi _api;
final Box<TranslationData> _localBox;
@override
Future<List<Ayah>> getTranslation(int surahNumber, String langCode) async {
final cacheKey = '${langCode}_surah_$surahNumber';
// Return cached data immediately if available
if (_localBox.containsKey(cacheKey)) {
return _localBox.get(cacheKey)!.ayahs;
}
// Fetch, cache, then return
try {
final data = await _api.getTranslation(surahNumber, langCode);
await _localBox.put(cacheKey, TranslationData(ayahs: data));
return data;
} catch (e) {
throw OfflineException('Translation not available offline yet.');
}
}
}After a user reads any Surah in any language, that content is cached forever. The second read is always instant — no network, no delay, no loading spinner.
The PDF Generator Nobody Expected
Three months after launch, my most requested feature was something I'd never planned: a combined PDF export — one document showing selected Surahs in multiple languages side by side.
I almost dismissed it as out of scope. A Quran app shouldn't need PDF generation. Then I saw how users were actually using the app: teachers printing lesson materials, students doing comparative translation study, families archiving specific Surahs for personal milestones.
Building this in Flutter, with no native bridge, was harder than the rest of the app combined.
The pdf package is excellent but not designed for complex RTL + LTR mixed layouts. Arabic text runs right-to-left. English translation runs left-to-right. They need to sit in the same row, aligned by Ayah number. The font rendering in PDF format differs from screen rendering — the Tashkeel spacing that looked right on screen created overlapping characters in the generated PDF.
The eventual approach:
- Each Ayah is laid out as a PDF row with up to 4 columns: Arabic, and up to 3 selected translations
- Arabic column is always right-aligned with extra vertical padding for Tashkeel
- Pages use A4 with 20mm margins — suitable for both screen and print
- The generation happens in a Dart isolate so the UI doesn't freeze on long Surahs
Future<Uint8List> generateQuranPdf({
required List<int> surahNumbers,
required List<String> translationCodes,
}) async {
return await compute(_generateInIsolate, {
'surahs': surahNumbers,
'translations': translationCodes,
});
}
Uint8List _generateInIsolate(Map<String, dynamic> params) {
final pdf = pw.Document();
// layout logic here
return pdf.save();
}Generation time for a full Surah Al-Baqarah (286 Ayahs) in 3 languages: 4.2 seconds. Not instant, but the progress indicator keeps users informed. Worth it — this feature has the highest user satisfaction rating of anything in the app.
CI/CD: Shipping to 4 Platforms Without Going Insane
Before I set up proper CI/CD, releasing a new version meant:
- Build for Android — 15 minutes
- Test on Android — manually, 30+ minutes
- Build for iOS — 20 minutes
- Test on iOS — manually, 30+ minutes
- Build for macOS, Windows, Web — another 40 minutes
- Upload to each store separately — 30 minutes
That's nearly 3 hours per release. I was releasing roughly once a week. That's 12 hours a month just on release logistics.
Now, a push to the main branch triggers a GitHub Actions workflow that:
- Runs all unit and widget tests
- Builds all 4 platform targets in parallel
- Publishes the web version to Netlify automatically
- Generates signed APK and IPA files as artifacts
- Creates a GitHub Release with changelogs auto-generated from commit messages
Total time: 18 minutes, while I'm doing something else. Manual effort: zero.
Building an Open Source Community
When I open-sourced the project, I expected maybe 5 stars and a few bug reports. What I didn't expect was contributors from 12 countries adding translations, fixing encoding issues I didn't know existed, and submitting pull requests for features I hadn't considered.
A developer in Indonesia noticed that the Javanese script rendering was incorrect and submitted a fix with a detailed explanation of the character encoding issue. A student in Turkey contributed a complete Ottoman Turkish translation that I didn't have the data for. Someone in France fixed a line-height issue with the Latin character diacritics in the French translation.
These are contributions I could not have made myself. They required cultural and linguistic knowledge that I simply don't have. Open source wasn't just a philosophical choice — it made the product measurably better.
If you're building something that serves a global, multilingual audience: open source it. The community knows things you don't.
Why It's Free — and Will Stay That Way
The question I get most: why not monetise it?
There are two honest answers.
The first is practical: the app's user base includes a significant portion of users in low-income countries where even a $1 one-time payment would exclude many of them. Any paywall would have the same effect as the apps I was frustrated with when I started building this.
The second is personal: some things shouldn't be behind a paywall. Access to one's religious text is one of them. I'm a Muslim developer. Building this app and keeping it free is, for me, an act of service — not a business decision.
The server costs (API hosting, Netlify, Firebase) run to around ₹3,000–4,000 per month. Donations from users cover most of this most months. When they don't, I cover the difference. That's a cost I'm comfortable with.
Frequently Asked Questions
How many languages and translations does Al Quran Multilingual support?
The app currently supports 90+ languages and 470+ translations, including Arabic, Urdu, English, Tamil, French, Malay, Turkish, Indonesian, German, Bengali, and many more. Translations are downloaded on demand and cached offline.
How does the app work offline?
The Arabic text is bundled with the app. All other translations are downloaded the first time you read them, then cached permanently on-device using Hive. After your first read of any Surah in any language, it's available offline forever, with no expiry.
Why is the app completely free with no ads?
Access to religious text shouldn't be behind a paywall or interrupted by ads. The app is free for everyone. Running costs are covered by voluntary donations from users. The open-source community also contributes features and bug fixes, which keeps the cost of development low.
How do I contribute a new translation or fix a bug?
The full source is on GitHub at github.com/jinosh05. Open an issue for bugs, or submit a pull request for translations and features. All contributions are reviewed within a week. Translation data is in structured JSON format — no Flutter knowledge required to add a translation.
What platforms does the app support?
iOS, Android, macOS, Windows, and Web — all from a single Flutter codebase. The web version is hosted on Netlify. Mobile and desktop apps are available through their respective app stores.
How does the PDF generator work?
Select any Surahs and up to 3 translations, then tap Export PDF. The generation runs in a background Dart isolate (so the UI stays smooth) and produces an A4 document with Arabic and selected translations laid out side by side by Ayah. Generation takes 4–6 seconds for long Surahs.