Setup
Install better_i18n and configure BetterI18nProvider in your Flutter app.
This guide walks you through installing better_i18n and setting up BetterI18nProvider in your Flutter app.
Prerequisite: Create a project at dash.better-i18n.com. Your project identifier will be in the format org/project (e.g., my-company/mobile-app).
Install
Add better_i18n to your pubspec.yaml:
dependencies:
better_i18n: ^0.1.0Then run:
flutter pub getFor offline caching (recommended), also add shared_preferences:
dependencies:
better_i18n: ^0.1.0
shared_preferences: ^2.3.0 # for SharedPrefsStorageshared_preferences is optional. Without it, translations are cached in memory only — suitable for development. For production, pass a SharedPrefsStorage() to enable offline support.
Wrap with BetterI18nProvider
Wrap your root widget (typically MaterialApp or CupertinoApp) with BetterI18nProvider:
import 'package:better_i18n/better_i18n.dart';
import 'package:flutter/material.dart';
void main() {
runApp(
BetterI18nProvider(
project: 'your-org/your-project',
defaultLocale: 'en',
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomeScreen(),
);
}
}BetterI18nProvider fetches translations from CDN when the app starts and rebuilds its subtree when the locale changes.
Translate
Use context.t('namespace.key') anywhere in the widget tree below BetterI18nProvider:
import 'package:better_i18n/better_i18n.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(context.t('common.appTitle')),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(context.t('common.welcome')),
// With interpolation arguments
Text(
context.t('common.greeting', args: {'name': 'Osman'}),
),
],
),
),
);
}
}Key format is "namespace.key" — matching your CDN translation structure. If the key is not found, the key itself is returned as fallback.
Switch Locale
Use context.setI18nLocale(code) to switch the active language at runtime:
import 'package:better_i18n/better_i18n.dart';
import 'package:flutter/material.dart';
class LanguagePicker extends StatelessWidget {
const LanguagePicker({super.key});
@override
Widget build(BuildContext context) {
final languages = context.i18nLanguages;
final currentLocale = context.i18nLocale;
return ListView.builder(
itemCount: languages.length,
itemBuilder: (context, index) {
final lang = languages[index];
return ListTile(
title: Text(lang.nativeName ?? lang.name ?? lang.code),
trailing: lang.code == currentLocale
? const Icon(Icons.check)
: null,
onTap: () => context.setI18nLocale(lang.code),
);
},
);
}
}context.i18nLanguages returns the language list from the CDN manifest — same languages you configure in the dashboard. No hardcoded lists needed.
With Offline Support
For production apps, enable offline caching and locale persistence:
import 'package:better_i18n/better_i18n.dart';
import 'package:flutter/material.dart';
void main() {
runApp(
BetterI18nProvider(
project: 'your-org/your-project',
defaultLocale: 'en',
storage: SharedPrefsStorage(),
loadingBuilder: (_) => const MaterialApp(
home: Scaffold(body: Center(child: CircularProgressIndicator())),
),
child: const MyApp(),
),
);
}See Offline & Caching for the full caching guide.