In the first article of this series, we were able to implement Flutter app localization, but the user was not able to switch from one language to another inside the app. In this tutorial, we will give the user the ability to change the language from within the application using the Riverpod package. We will also save the selected locale so that when the user opens the app, the selected language is what will be shown. To do this, we need to use the SharedPreference library. Without further ado, let’s get started.
By the end of this article, you should have a localized Flutter app with support for English, French, and Arabic languages as shown below. Also, the user should be able to switch between the supported languages and have their preferred language saved.
To complete this tutorial you will need to:
Download and install Android Studio or Visual Studio Code
Download and install Flutter.
Set up your editor as described here.
Clone and run part one from here.
Once you have cloned the first part, run “flutter pub get” and make sure there is no error.
Open the pubspec.yaml and add the necessary dependencies below:
dependencies:
flutter_riverpod: ^0.14.0+3
shared_preferences: ^2.0.7
To include the package in your project, run the command “flutter pub get” on your terminal.
Create a dart file call supported_locale.dart. Here we will have our custom supported locale as an enum as follows;
enum SupportedLocale { en, fr, ar }
Also, add an extension method to get the name and the code like this;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
extension SupportedLocalExtension on SupportedLocale { String get code => this.toString() .split('.') .last; String get name { String name; switch (this) { case SupportedLocale.en: name = 'English'; break; case SupportedLocale.fr: name = 'French'; break; case SupportedLocale.ar: name = 'Arabic'; break; } return name; } }
Next, we will write the shared preference class to save and load our locale from the SharedPreference library. Create a new dart file and name it shared_pref.dart. Include the following code inside shared_pref.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import 'dart:ui';
import 'package:shared_preferences/shared_preferences.dart';
import 'supported_locale.dart';
class AppSharedPreference {
static SharedPreferences sharedPreference;
static
const _LOCALE_KEY = 'locale';
static Future init() async {
sharedPreference = await SharedPreferences.getInstance();
}
static void saveLocale(SupportedLocale locale) {
sharedPreference.setString(_LOCALE_KEY, locale.code);
}
static Locale getLocale() {
final String localeCode = sharedPreference.get(_LOCALE_KEY) ?? ' ';
if (localeCode.isEmpty) throw 'Locale not found';
return Locale(localeCode);
}
}
Next, we write the locale notifier class that contains the logic to switch between the supported languages. It also contains the logic to save and retrieve the locale from our AppSharedPreference class.
Create a dart file and call it local_provider.dart, add the following code to it;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class LocaleNotifier extends StateNotifier < Locale > {
LocaleNotifier(): super(Locale('en')) {
onAppStart();
}
void changeLanguage(SupportedLocale locale) {
try {
AppSharedPreference.saveLocale(locale);
state = Locale(locale.code);
} catch (error) {
state = Locale('en');
}
}
void onAppStart() {
try {
final locale = AppSharedPreference.getLocale();
state = locale;
} catch (error) {
state = Locale('en');
}
}
}
final localeProvider = StateNotifierProvider < LocaleNotifier, Locale > ((ref) {
return LocaleNotifier();
});
1 2 3 4 5
void main() async { WidgetsFlutterBinding.ensureInitialized(); await AppSharedPreference.init(); runApp(ProviderScope(child: LocalizationDemo())); }
Inside the AppBar, add the PopupMenuButton to select the desired language.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
AppBar( title: Text('{LocalizationHelper.of(context).appName}'), actions: [ PopupMenuButton < SupportedLocale > ( itemBuilder: (context) { return SupportedLocale.values .map < PopupMenuEntry < SupportedLocale >> ((e) => PopupMenuItem( child: Text('{e.name}'), value: e, )) .toList(); }, onSelected: (locale) { context.read(localeProvider.notifier) .changeLanguage(locale); }, ) ], )
Wrap the MaterialApp with the consumer class from Riverpod to get the selected locale, as follows;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Consumer( builder: (BuildContext context, T Function < T > (ProviderBase < Object, T > ) watch, Widget child) { final locale = watch(localeProvider); return MaterialApp( title: 'Localization Demo', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), locale: locale, supportedLocales: [ Locale('en', 'US'), Locale('fr', 'FR'), Locale('ar'), ], localizationsDelegates: [ GlobalCupertinoLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, LocalizationHelper.delegate ], home: child, ); }, child: HomePage(), )
Congratulations, you now have a Flutter app that is localized in three different languages. Also, there is now a popup menu button for the user to select a particular language. The selected language will then be the default language of the app and get loaded when the app is reopened.
Github: https://github.com/De-Morgan/localiztion-demo/tree/master