RTL and localization setup
Locale-aware MaterialApp with a hand-written localisation delegate, switchable language and direction-safe layout helpers.
FlutterInternationalisationflutterl10nrtllocale
Code
import 'package:flutter/widgets.dart';
class AppStrings {
AppStrings(this.locale);
final Locale locale;
static const supported = [Locale('en'), Locale('ar')];
static const _table = <String, Map<String, String>>{
'en': {'book': 'Book now', 'greeting': 'Hello, {name}'},
'ar': {'book': 'احجز الآن', 'greeting': 'مرحبا، {name}'},
};
static AppStrings of(BuildContext c) => Localizations.of<AppStrings>(c, AppStrings)!;
String _t(String k) => _table[locale.languageCode]?[k] ?? _table['en']![k]!;
String get book => _t('book');
String greeting(String name) => _t('greeting').replaceAll('{name}', name);
static const delegate = _Delegate();
}
class _Delegate extends LocalizationsDelegate<AppStrings> {
const _Delegate();
@override
bool isSupported(Locale l) => AppStrings.supported.any((s) => s.languageCode == l.languageCode);
@override
Future<AppStrings> load(Locale l) async => AppStrings(l); // synchronous table
@override
bool shouldReload(_Delegate old) => false;
}
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'app_strings.dart';
class LanguageApp extends StatefulWidget {
const LanguageApp({super.key});
static void setLocale(BuildContext c, Locale l) {
final state = c.findAncestorStateOfType<_LanguageAppState>()!;
// ignore: invalid_use_of_protected_member
state.setState(() => state._locale = l);
}
@override
State<LanguageApp> createState() => _LanguageAppState();
}
class _LanguageAppState extends State<LanguageApp> {
Locale? _locale; // null follows the device language
@override
Widget build(BuildContext context) {
return MaterialApp(
locale: _locale,
supportedLocales: AppStrings.supported,
localizationsDelegates: const [
AppStrings.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate, // supplies text direction
GlobalCupertinoLocalizations.delegate,
],
home: const _Home(),
);
}
}
class _Home extends StatelessWidget {
const _Home();
@override
Widget build(BuildContext context) {
final s = AppStrings.of(context);
return Scaffold(
appBar: AppBar(title: Text(s.greeting('Sam')), actions: [
TextButton(onPressed: () => LanguageApp.setLocale(context, const Locale('ar')), child: const Text('AR')),
TextButton(onPressed: () => LanguageApp.setLocale(context, const Locale('en')), child: const Text('EN')),
]),
// Padding and alignment use Directional types so they mirror in RTL.
body: Padding(
padding: const EdgeInsetsDirectional.only(start: 24, top: 24),
child: Align(alignment: AlignmentDirectional.topStart, child: FilledButton(onPressed: () {}, child: Text(s.book))),
),
);
}
}
How it works
What it solves
Apps for salons and similar local services are often used in Arabic or Hebrew as well as English. Supporting that is not just translating text: the whole layout must mirror and strings must be reachable from any widget.
How it works
AppStrings is a minimal localisation class: a map per language, a fallback to English, and a placeholder replacement for names. Its LocalizationsDelegate tells Flutter which languages are supported and loads the strings.
LanguageApp registers that delegate next to the framework delegates. GlobalWidgetsLocalizations is the one that supplies the text direction, so Arabic automatically flips the app bar, drawers and back arrows. A nullable locale follows the device until the user picks a language from the buttons.
Adapting it
For many strings or plural rules, move to Flutter's gen_l10n with ARB files; the usage pattern (context.l10n.book) stays the same. Replace the state lookup with a provider or a controller if you persist the choice. Always write padding and alignment with their Directional versions.
Pitfalls
- Add the
flutter_localizationsSDK dependency andintl, otherwise the framework delegates do not compile. - Numbers and dates are formatted by
intlper locale; do not build them by string concatenation. - Icons with direction, such as arrows, need
matchTextDirectionor theDirectionalitycheck.
How to use it
Add to pubspec.yaml: flutter_localizations with sdk: flutter, plus intl. Copy both files into lib/l10n/.
void main() => runApp(const LanguageApp());
Read text with AppStrings.of(context).book from any widget.
Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.