In this article I will be Implementing Internationalizing Flutter app from anywhere (within the app) with the help of arb files, plain InheritedWidget. I’ll be taking advantage and building on a previous article Flutter Localization with intl https://programtom.com/dev/2024/10/14/flutter-localization-with-intl/.
MaterialApp fields
Locale in a Flutter App is set through a locale field present in the Master widgets MaterialApp and CupertinoApp. A Very minimal Root Widget would look like:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) => LocaleManagerWidget(
builder: (context, locale) => MaterialApp(
locale: locale,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.black),
useMaterial3: true,
extensions: [
WidgetToolkitTheme.light(),
ItemPickerTheme.light(),
]),
home: const LoadingScreen(),
),
);
}
(Custom) LocaleManagerWidget
I’ve coded a StatefulWidget that handles
- Loading State from cache at start
- Update Locale after change from inner Widgets,
- Pass down the locale – so the Master Widget may delegate it to the engine. I’m using a custom Widget Builder
import 'package:flutter/material.dart';
import '../common/ds/shared_preferences_data_source.dart';
import 'local_manager_inherited_widget.dart';
class LocaleManagerWidget extends StatefulWidget {
final Function(dynamic context, Locale locale) builder;
const LocaleManagerWidget({
super.key,
required this.builder,
});
@override
State<LocaleManagerWidget> createState() => _LocaleManagerWidgetState();
}
class _LocaleManagerWidgetState extends State<LocaleManagerWidget> {
Locale _locale = const Locale('en'); // Default locale
void _setLocale(Locale locale) {
setState(() {
_locale = locale;
});
}
@override
void initState() {
_load();
super.initState();
}
Future<void> _load() async {
var userLanguage = await SharedPreferencesDataSource().get("userLanguage");
setState(() {
_locale = Locale(userLanguage ?? 'en');
});
}
@override
Widget build(BuildContext context) => LocaleManagerInheritedWidget(
locale: _locale,
setLocale: _setLocale,
child: widget.builder(context, _locale),
);
}
I’ll not get into the Data Source as it may be memory, file, shared_preferences, secure_storage, or whatever you’d like.
LocaleManagerInheritedWidget
Flutter Engineers around the world has gone a long way to complicate building on InheritedWidget to create State Management solutions on
- Provider
- Bloc
- Riverpod
- ioc_contaienr
- and more
A simple way to expose something to the child widgets – get or setting locale look like this:
import 'package:flutter/material.dart';
class LocaleManagerInheritedWidget extends InheritedWidget {
final Locale locale;
final Function(Locale) setLocale;
const LocaleManagerInheritedWidget({
super.key,
required this.locale,
required this.setLocale,
required super.child,
});
static LocaleManagerInheritedWidget? of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<LocaleManagerInheritedWidget>();
}
@override
bool updateShouldNotify(covariant LocaleManagerInheritedWidget oldWidget) {
return locale != oldWidget.locale;
}
}
Accessing Internationalizing data – from nodes in the Flutter Widget Tree
Accessing nodes in any widget underneed is as plain as
LocaleManagerInheritedWidget.of(context)?.setLocale(Locale(newLang)); //or LocaleManagerInheritedWidget.of(context)?.locale // to get it