In the settings page of my app, I would like to add an option that controls the app language.
I can set the language before starting the app like this:
You can wrap the MaterialApp
widget with a ChangeNotifierProvider
and a Consumer
widgets and control the language from the model.
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
builder: (context) => MainModel(context: context),
child: Consumer<MainModel>(builder: (context, mainModel, child) {
return MaterialApp(
locale: Locale(mainModel.preferredLanguageCode),
....
On the MainModel
, all you need to do is change the preferredLanguageCode
variable to whatever you want ('en', 'ar', 'es', etc). Don't forget to call NotifyListeners()
once you change the language.
This and the other answer have only one problem: Any context
above MaterialApp
can't get the device language (for example when the app is started for the first time) with Localizations.localeOf(context)
. This method required a context
bellow MaterialApp
.
To fix this issue, I used this plugin to get the device language without the need of a context
.
Once the app starts, you can change the language any way you want that this approach will work. I also use SharedPreferences
to store the preferred language once the user changes it.
It's easier to use easy_localization package.
For changing language, for example:
onTap: (){
EasyLocalization.of(context).locale = Locale('en', 'US');
}
I learned using this package by this video: Youtube Video Link
Wrap your MaterialApp
into a StreamBuilder
which will be responsible for providing the Locale
value to your application. And it will enable you to dynamically change it without restarting your app. This is an example using the rxdart package to implement the stream:
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: setLocale,
initialData: Locale('ar',''),
builder: (context, localeSnapshot) {
return MaterialApp(
// other arguments
locale: localeSnapshot.data,
);
}
);
}
Stream<Locale> setLocale(int choice) {
var localeSubject = BehaviorSubject<Locale>() ;
choice == 0 ? localeSubject.sink.add( Locale('ar','') ) : localeSubject.sink.add( Locale('en','') ) ;
return localeSubject.stream.distinct() ;
}
The above demonstration is just a basic way of how to achieve what you want to, but for a proper implementation of streams in your app you should consider using app-wide BloCs, which will significantly improve the quality of your app by reducing the number of unnecessary builds.
If you want to change app language without restarting the app and also without any plugin, you can follow the bellow steps:
In main file of the application, change the default MyHomePage
to a StatefullWidget
, in StatefullWedget
for example MyHomePage
create a static
method setLocal
as follow
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
static void setLocale(BuildContext context, Locale newLocale) async {
_MyHomePageState state = context.findAncestorStateOfType<_MyHomePageState>();
state.changeLanguage(newLocale);
}
@override
_MyHomePageState createState() => _MyHomePageState();
}
where _MyHomePageState
is the state
of your MyHomePage
widget
In your state
create a static
method changeLanguage
:
class _MyHomePageState extends State<MyHomePage> {
Locale _locale;
changeLanguage(Locale locale) {
setState(() {
_locale = locale;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Afghanistan',
theme: ThemeData(primaryColor: Colors.blue[800]),
supportedLocales: [
Locale('fa', 'IR'),
Locale('en', 'US'),
Locale('ps', 'AFG'),
],
locale: _locale,
localizationsDelegates: [
AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
localeResolutionCallback: (locale, supportedLocales) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale.languageCode &&
supportedLocale.countryCode == locale.countryCode) {
return supportedLocale;
}
}
return supportedLocales.first;
},
initialRoute: splashRoute,
onGenerateRoute: Router.generatedRoute,
);
}
}
Now from pages of your application you can change the language by calling the setLocal
method and pass a new Locale
as follow:
Locale newLocale = Locale('ps', 'AFG');
MyHomePage.setLocale(context, newLocale);
Please remember you need to create a LocalizationDelegate
,
Here is the link to the Written Tutorial and Demo Application