31

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:

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // other arguments
      locale: Locale('ar'),
    );
  }

But is it possible to change the language without restarting the app?

Raed Mughaus
  • 975
  • 1
  • 9
  • 15
  • 1
    Intresting, I remember on Android It's quite a hassle to change language without loosing state. But it could be done. – Haroun Hajem Apr 28 '19 at 11:49

5 Answers5

46

If you want to change app language without restarting the app and also without any plugin, you can follow the bellow steps:

  1. 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

  1. 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,
          );
       }
      }
    
  2. 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);
    
  3. Please remember you need to create a LocalizationDelegate,

  4. Here is the link to the Written Tutorial and Demo Application

Void
  • 969
  • 1
  • 11
  • 19
Seddiq Sorush
  • 2,709
  • 2
  • 20
  • 20
20

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.

Lennaert Bel
  • 318
  • 1
  • 6
Mazin Ibrahim
  • 7,433
  • 2
  • 33
  • 40
6

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.

Dpedrinha
  • 3,741
  • 3
  • 38
  • 57
6

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

UPDATE:

In version 3.0.0:

EasyLocalization.of(context).setLocale(Locale('en', '')); 
SardorbekR
  • 1,388
  • 3
  • 18
  • 33
  • 3
    This should be used with the latest version of easy localization (3.0.0) EasyLocalization.of(context).setLocale(Locale('en', '')); – Pratik Baid Jun 23 '21 at 13:17
1

You can use the most popular GetX library as well.

Call Get.updateLocale(locale) to update the locale. Translations then automatically use the new locale.

var locale = Locale('en', 'US');
Get.updateLocale(locale);
krsakju
  • 554
  • 5
  • 11
  • Hey, I used a more traditional approach in app localization, and recently I used getx as my main state manager. So I tried for hours to make the localization works and I found this answer that solves my question. But can you tell that there is any specific way to change the text language as well without using `AppLocalizations.of(context)!;` – Sayuru_Sandaru Oct 17 '22 at 01:14