When building multilingual Flutter apps, the usual go-to for localization is .arb
or .json
files. But what if your app needs something more dynamic? What if translations can be edited by an admin? Or fetched from your backend? That’s where SQLite
shines. 💡
In this article, we’ll walk through setting up a SQLite-powered translation system in Flutter that’s flexible, updatable at runtime, and great for user- or admin-generated content. Ready? Let’s go!
🛠️ Step 1: Add Dependencies
First, pull in the packages you’ll need:
dependencies:
sqflite: ^2.4.2
path_provider: ^2.1.5
flutter_localizations:
sdk: flutter
sqflite
helps us talk to the local database, and path_provider
gives us the app’s file path to store it.
flutter_localizations: https://docs.flutter.dev/ui/accessibility-and-internationalization/internationalization , https://pub.dev/packages/flutter_localization
sqflite: https://pub.dev/packages/sqflite
path_provider: https://pub.dev/packages/path_provider
🧱 Step 2: Create a Translations Table
Define your schema like this:
CREATE TABLE translations (
id INTEGER PRIMARY KEY,
locale TEXT,
key TEXT,
value TEXT
);
This simple table holds everything you need: the language (locale
), the key (key
), and its translation (value
).
📦 Step 3: Translation Service
Let’s load the translations into memory so we can use them in the app:
class TranslationService {
final Database db;
TranslationService(this.db);
Future<Map<String, String>> loadTranslations(String locale) async {
final List<Map<String, dynamic>> result = await db.query(
'translations',
where: 'locale = ?',
whereArgs: [locale],
);
return {
for (var row in result) row['key'] as String: row['value'] as String,
};
}
}
This service pulls all translation entries for a given locale and returns them as a Map.
🔌 Step 4: Custom Localization Delegate
Instead of using .arb
files, we’ll build a LocalizationsDelegate
that loads from SQLite.
class SQLiteLocalizationDelegate extends LocalizationsDelegate<SQLiteLocalizations> {
final TranslationService service;
SQLiteLocalizationDelegate(this.service);
@override
bool isSupported(Locale locale) => true;
@override
Future<SQLiteLocalizations> load(Locale locale) async {
final translations = await service.loadTranslations(locale.languageCode);
return SQLiteLocalizations(translations);
}
@override
bool shouldReload(covariant LocalizationsDelegate old) => false;
}
class SQLiteLocalizations {
final Map<String, String> _localizedStrings;
SQLiteLocalizations(this._localizedStrings);
String translate(String key) => _localizedStrings[key] ?? key;
static SQLiteLocalizations of(BuildContext context) =>
Localizations.of<SQLiteLocalizations>(context, SQLiteLocalizations)!;
}
With this setup, your app knows how to fetch translations from the database based on the current locale.
🏁 Step 5: Plug It Into Your App
Now wire everything up in your MaterialApp
:
MaterialApp(
localizationsDelegates: [
SQLiteLocalizationDelegate(translationService),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [Locale('en'), Locale('es')],
home: MyHomePage(),
);
And wherever you need a translation:
Text(SQLiteLocalizations.of(context).translate('hello'));
Boom 💥 — your app is now powered by dynamic, database-driven translations!
✨ Why Go with SQLite?
Using SQLite gives you:
✅ Runtime translation updates
✅ Easy syncing with backend-localized data
✅ A clean, scalable structure for multi-language apps
This is especially useful for apps with content that evolves over time or needs customization per user.
Let's Connect! 🎉
If you found this helpful and want to stay updated with more Flutter tips, feel free to follow me:
📘 Dev.to: https://dev.to/thinnakrit
💼 LinkedIn: https://www.linkedin.com/in/thinnakrit
💻 GitHub: https://github.com/thinnakrit
Happy coding! 😊
Top comments (0)