DEV Community

Cover image for 🧩 Ditch the .arb: Using SQLite for Flutter Translations Like a Pro
Thinnakrit
Thinnakrit

Posted on

🧩 Ditch the .arb: Using SQLite for Flutter Translations Like a Pro

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
Enter fullscreen mode Exit fullscreen mode

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
);

Enter fullscreen mode Exit fullscreen mode

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,
    };
  }
}

Enter fullscreen mode Exit fullscreen mode

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)!;
}

Enter fullscreen mode Exit fullscreen mode

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(),
);

Enter fullscreen mode Exit fullscreen mode

And wherever you need a translation:

Text(SQLiteLocalizations.of(context).translate('hello'));

Enter fullscreen mode Exit fullscreen mode

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! 😊

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

Sentry image

Make it make sense

Make sense of fixing your code with straight-forward application monitoring.

Start debugging →