Resolving AppLocalizations Without Any BuildContext Around
Setting up Android notification channels before invoking runApp
When you read documentation about flutter_localizations you’ll notice, that it’s fairly easy to use the localizations with AppLocalizations.of(context)
. As this is used when creating widgets within a build tree, you might wonder why this could be an issue, but when you follow the example for setting up the firebase messaging SDK, especially creating the notification channels for Android, you’ll ask yourself:
Where do I get the context from?
Eventually runApp()
hasn’t been called yet.
void main() {
final l10n = AppLocalizations.of(???);
setupFirebaseMessaging(l10n);
runApp(App());
}
Looking into the AppLocalizations delegate
We’ll have to get the AppLocalizations
instance differently then. Let’s have a look at how these are created; we don’t even have to look far from what we already know of the Flutter localization itself. The AppLocalizations.delegate
defines a simple function load(locale)
, which creates locale dependent instances of AppLocalizations
(or throws a FlutterError
if the provided locale isn’t supported).
void main() async {
final locale = ???;
final l10n = await AppLocalizations.delegate.load(locale);
...
}
Now we only need to find the best locale to use for the localizations.
Resolving the supported user locale
To resolve the user locale from the list of all supported locales, we have to find a best fit for one of the preferred system defined user locales. This is implemented with the WidgetsApp
and usually used by the Flutter localization implementation directly:
When a localeListResolutionCallback is provided, Flutter will first attempt to resolve the locale with the provided localeListResolutionCallback. If the callback or result is null, it will fallback to trying the localeResolutionCallback. If both localeResolutionCallback and localeListResolutionCallback are left null or fail to resolve (return null), basic fallback algorithm will be used. [Flutter]
In most cases it should be sufficient to just use the basic fallback algorithm implemented with the basicLocaleListResolution()
function. When you need an algorithm to implement a complete locale resolution as defined with Unicode TR35, you’ll have to provide and use a custom implementation with the localeListResolutionCallback
function.
Putting everything together
So now that we know how to create the AppLocalizations
instance and how to resolve the supported user locale for it, let’s have a look how it all works together in the end. We’ll just declare it as a global property l10n
here:
Future<AppLocalizations> get l10n {
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
final preferred = widgetsBinding.window.locales;
const supported = AppLocalizations.supportedLocales;
final locale = basicLocaleListResolution(preferred, supported);
return AppLocalizations.delegate.load(locale);
}
Just consider that this implementation is not stateful and should not be used from within a repository or any other abstraction. Consider using a global key or another resolvable value instead.
Eventually, you wouldn’t want to have a string defined in a wrong locale, whenever users decide to change the locale of their device (even if it’s only one in a million).