Botón para cambiar el idioma Flutter

12 minutos de lectura

Estoy creando una aplicación en Flutter, hasta ahora estoy usando la internacionalización con JSON donde el idioma de la aplicación se basa en el idioma que el usuario tiene como predeterminado en su teléfono funciona bastante bien, pero me gustaría darle al usuario una oportunidad de cambiar el idioma sin cambiar la configuración de idioma del sistema del teléfono, simplemente haciendo clic en un botón y luego la aplicación cambia el idioma sin pasar por la configuración.

Aquí está el código:

El principal:

import 'package:flutter/material.dart';
import 'package:flutter_app_darkmode/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

  void main() => runApp(MyApp());

  class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) => MaterialApp(
      supportedLocales: [
      Locale('en', "ZA"),
      Locale('pt', "MZ"),
      ],
      localizationsDelegates: [
      AppLocalizations.delegate,
      GlobalMaterialLocalizations.delegate,
      GlobalWidgetsLocalizations.delegate
      ],
      localeResolutionCallback: (locale, supportedLocales) {
       for (var supportedLocale in supportedLocales) {
        if (supportedLocale.languageCode == locale.languageCode &&
            supportedLocale.countryCode == locale.countryCode) {
          return supportedLocale;
          } else {
          if (MyHomePage.local != null) {
            for (int i = 0; i < supportedLocales.length; i++) {
              if (MyHomePage.local == supportedLocales.elementAt(i)) {
                return supportedLocales.elementAt(i);
              }}}}}
            return supportedLocales.first;
            },
           home: MyHomePage(),
          );}

         class MyHomePage extends StatefulWidget {
         @override
         _MyHomePageState createState() => _MyHomePageState();

           class _MyHomePageState extends State<MyHomePage> {
           getLocale() {
           Locale myLocale = Localizations.localeOf(context);
            print(myLocale);}

            @override
             Widget build(BuildContext context) {
              getLocale();
               return Scaffold(
                body: Center(
                child: Padding(
                 padding: const EdgeInsets.all(8.0),
                  child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                   Text(
                   AppLocalizations.of(context).translate('first_string'),
                   style: TextStyle(fontSize: 25),
                   textAlign: TextAlign.center,),
                  Text(
                  AppLocalizations.of(context).translate('second_string'),
                    style: TextStyle(fontSize: 25),
                    textAlign: TextAlign.center,),
                    RaisedButton(
                      child: Text('PT'),
                    onPressed: () {},
                     ),],),),),);}}

La clase app_ubicaciones:

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class AppLocalizations {
final Locale locale;

  AppLocalizations(this.locale);

  static AppLocalizations of(BuildContext context) {
   return Localizations.of<AppLocalizations>(context, AppLocalizations);
  }

    static const LocalizationsDelegate<AppLocalizations> delegate =
    _AppLocalizationsDelegate();

    Map<String, String> _localizedStrings;

       Future<bool> load() async {
       String jsonString =
        await rootBundle.loadString('lang/${locale.languageCode}.json');

        Map<String, dynamic> jsonMap = json.decode(jsonString);

       _localizedStrings = jsonMap.map((key, value) {
       return MapEntry(key, value.toString());
        });

        return true;}

       String translate(String key) {
      return _localizedStrings[key];}}

     class _AppLocalizationsDelegate
     extends LocalizationsDelegate<AppLocalizations> {
     const _AppLocalizationsDelegate();

       @override
       bool isSupported(Locale locale) {
       return ['en', 'pt'].contains(locale.languageCode);}

       @override
       Future<AppLocalizations> load(Locale locale) async {
       AppLocalizations localizations = new AppLocalizations(locale);
       await localizations.load();
       return localizations;}

        @override
        bool shouldReload(_AppLocalizationsDelegate old) => false;}

avatar de usuario de spkersten
spkersten

Puede configurar el locale propiedad de MaterialApp con su método de gestión de estado favorito. Por ejemplo:

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();

  static _MyAppState of(BuildContext context) => context.findAncestorStateOfType<_MyAppState>();
}

class _MyAppState extends State<MyApp> {
  Locale _locale;

  void setLocale(Locale value) {
    setState(() {
      _locale = value;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: _locale,
      home: Dashboard(),
    );
  }
}

class Dashboard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextButton(
          child: Text("Set locale to German"),
          onPressed: () => MyApp.of(context).setLocale(Locale.fromSubtags(languageCode: 'de')),
        ),
        TextButton(
          child: Text("Set locale to English"),
          onPressed: () => MyApp.of(context).setLocale(Locale.fromSubtags(languageCode: 'en')),
        ),
      ],
    );
  }
}

  • esto funcionó para mí! Con una pequeña modificación: no pude hacer que funcionara cuando mi FlutterApp tenía un StatfulWidget (por ejemplo, MyApp) como ‘inicio’ principal. Así que envolví el widget MyApp con estado en un widget sin estado (por ejemplo, MyAppWrapper). Lo único que hace MyAppWrapper es que tiene un método de compilación que devuelve MyApp().

    – drpawelo

    2 de julio de 2021 a las 16:16

  • pero esto no sobreviviría a los reinicios de la aplicación, ¿verdad?

    – Marco

    16 de julio de 2021 a las 16:51

  • @Marcus De hecho, no sobrevive a los reinicios de la aplicación. Tendrías que persistir los datos.

    – habla

    12 de agosto de 2021 a las 11:51

  • Cualquier ejemplo que persista los cambios.

    – AprendeFlutter

    12 de abril de 2022 a las 13:07

  • @LearnFlutter podrías usar el Preferencias compartidas paquete y conservar los datos allí cuando cambia la selección. Luego, para leer los datos, podrías hacer esto: @override void didChangeDependencies() { SharedPreferencesHelper.getLanguageCode().then((languageCode) { setState(() { _locale = Locale(languageCode); }); }); super.didChangeDependencies(); }

    – Sam Bains

    21 de mayo de 2022 a las 9:17


Avatar de usuario de Balaji Venkatraman
Balaji Venkatraman

Tienes que usar las localizaciones proporcionadas por Flutter. Debe usar archivos JSON y delegado personalizados para los idiomas admitidos. implementé usando bloc

Pasos a seguir,

  1. crear una carpeta assets/languages/ en la carpeta raíz
  2. Crear JSON archivos para los idiomas admitidos. Me gusta: en.json, es.json
  3. Cree pares de clave y valor para sus cadenas en cada archivo de acuerdo con sus cadenas de idioma específicas
  4. En main.dart crear por defecto locale, supportedLocales and localizationsDelegates.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

import 'package:movie_app/common/constants/languages.dart';
import 'package:movie_app/presentation/app_localizations.dart';
import 'package:movie_app/presentation/blocs/language/language_bloc.dart';
import 'package:movie_app/presentation/journeys/home/home_screen.dart';

class MovieApp extends StatefulWidget {
  @override
  _MovieAppState createState() => _MovieAppState();
}

class _MovieAppState extends State<MovieApp> {
  LanguageBloc _languageBloc;

  @override
  void initState() {
    _languageBloc = LanguageBloc();
    super.initState();
  }

  @override
  void dispose() {
    _languageBloc.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return BlocProvider<LanguageBloc>.value(
      value: _languageBloc,
      child: BlocBuilder<LanguageBloc, LanguageState>(
        builder: (context, state) {
          if (state is LanguageLoaded) {
            return MaterialApp(
                debugShowCheckedModeBanner: false,
                title: 'Movie App',
                home: HomeScreen(),
                supportedLocales:
                    Languages.languages.map((e) => Locale(e.code)).toList(),
                locale: state.locale,
                localizationsDelegates: [
                  AppLocalizations.delegate,
                  GlobalMaterialLocalizations.delegate,
                  GlobalWidgetsLocalizations.delegate,
                ],
              );
          }
          return SizedBox.shrink();
        },
      ),
    );
  }
}
  1. Ahora crea modelos y constantes de lenguajes.
class LanguageEntity {
  final String code;
  final String value;

  const LanguageEntity({
     this.code,
     this.value,
  });
}

class Languages {
  const Languages._();

  static const languages = [
    LanguageEntity(code: 'en', value: 'English'),
    LanguageEntity(code: 'es', value: 'Spanish'),
  ];
}

  1. Ahora Crear delegado de localización de aplicaciones
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:movie_app/common/constants/languages.dart';

class AppLocalizations {
  final Locale locale;

  AppLocalizations(this.locale);

  static const LocalizationsDelegate<AppLocalizations> delegate =
      _AppLocalizationDelagate();

  static AppLocalizations of(context) =>
      Localizations.of<AppLocalizations>(context, AppLocalizations);

  Map<String, String> _localisedString;

  Future<bool> load() async {
    final jsonString = await rootBundle
        .loadString('assets/languages/${locale.languageCode}.json');
    final Map<String, dynamic> jsonMap = json.decode(jsonString);
    _localisedString =
        jsonMap.map((key, value) => MapEntry(key, value.toString()));
    return true;
  }

  String translate(String key) {
    return _localisedString[key];
  }
}

class _AppLocalizationDelagate extends LocalizationsDelegate<AppLocalizations> {
  const _AppLocalizationDelagate();

  @override
  bool isSupported(Locale locale) {
    return Languages.languages
        .map((e) => e.code)
        .toList()
        .contains(locale.languageCode);
  }

  @override
  bool shouldReload(covariant LocalizationsDelegate old) {
    return false;
  }

  @override
  Future<AppLocalizations> load(Locale locale) async {
    AppLocalizations appLocalizations = AppLocalizations(locale);
    await appLocalizations.load();
    return appLocalizations;
  }
}
  1. Ahora crea bloques
import 'dart:async';

import 'package:bloc/bloc.dart';
// import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:movie_app/common/constants/languages.dart';
import 'package:movie_app/domain/entities/language_entity.dart';

part 'language_event.dart';
part 'language_state.dart';

class LanguageBloc extends Bloc<LanguageEvent, LanguageState> {
  LanguageBloc() : super(LanguageLoaded(Locale(Languages.languages[0].code)));

  @override
  Stream<LanguageState> mapEventToState(
    LanguageEvent event,
  ) async* {
    if (event is ToggleLanguageEvent) {
      yield LanguageLoaded(Locale(event.language.code));
    }
  }
}

8.Ahora crea un evento

part of 'language_bloc.dart';

abstract class LanguageEvent {
  const LanguageEvent();
}

class ToggleLanguageEvent extends LanguageEvent {
  final LanguageEntity language;

  ToggleLanguageEvent(this.language);
}
  1. Ahora crea el estado
part of 'language_bloc.dart';

abstract class LanguageState {
  const LanguageState();

}

class LanguageLoaded extends LanguageState {
  final Locale locale;

  LanguageLoaded(this.locale);
}

10.Ahora botón Crear para cambiar de idioma.

RaisedButton(child: ,RaisedButton(child: Text('Switch', 
     onPressed: (int index) {
        BlocProvider.of<LanguageBloc>(context).add(
          ToggleLanguageEvent(
            Languages.languages[index], // index value can be 0 or 1 in our case
          ),                            // 0 - en, 1 - es
        );
        Navigator.of(context).pop();
      },
);

Además, consulte el enlace para una implementación clara
https://www.youtube.com/watch?v=W-2p3zB1z8k

Si está utilizando el proveedor como administración estatal, puede seguir este artículo.

https://medium.com/flutter-community/flutter-internationalization-the-easy-way-using-provider-and-json-c47caa4212b2

Avatar de usuario de AnasSafi
AnasSafi

Debido a que la respuesta aceptada tiene algunas deficiencias, estableceré una nueva:

El cambio de configuración regional de la aplicación se puede hacer de varias maneras, aquí mostraré dos formas:

Camino 1 (usando rxdart y SharedPreferences complementos):

Para una mayor comprensión, se creó un proyecto para explicar cómo hacer eso, puede encontrarlo en https://github.com/AnasSafi/flutter_localization_example

Salida del proyecto:

ingrese la descripción de la imagen aquí


Forma 2:

Nota 1: yo estaba usado Preferencias compartidas complemento para guardar la configuración regional que seleccione el cliente.

Nota 2: Reemplazar Arkansas y PD con su configuración regional predeterminada…

Código completo de dardo principal archivo:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized(); //To solve problem (ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized)
  runApp(MainApp());
}

class MainApp extends StatefulWidget {
  const MainApp({Key? key}) : super(key: key);
  
  /*
  To Change Locale of App
   */
  static void setLocale(BuildContext context, Locale newLocale) async {
    _MainAppState? state = context.findAncestorStateOfType<_MainAppState>();

    var prefs = await SharedPreferences.getInstance();
    prefs.setString('languageCode', newLocale.languageCode);
    prefs.setString('countryCode', "");

    state?.setState(() {
      state._locale = newLocale;
    });
    
  }

  @override
  _MainAppState createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
  Locale _locale = Locale('ar', 'ps');

  @override
  void initState() {
    super.initState();
    this._fetchLocale().then((locale) {
      setState(() {
        this._locale = locale;
      });
    });
  }


  /*
  To get local from SharedPreferences if exists
   */
  Future<Locale> _fetchLocale() async {
    var prefs = await SharedPreferences.getInstance();

    String languageCode = prefs.getString('languageCode') ?? 'ar';
    String countryCode = prefs.getString('countryCode') ?? 'ps';

    return Locale(languageCode, countryCode);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: _locale,
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
        const FallbackCupertinoLocalisationsDelegate(),
      ],
      supportedLocales: [
        const Locale('en', ''), // English, no country code
        const Locale('ar', ''), // Arabic, no country code
      ],
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,
      ),
      home: InitializeApp(), // here use your own home name...
    );
  }
}


/*
  To solve problem of hold press on inputs
 */
class FallbackCupertinoLocalisationsDelegate extends LocalizationsDelegate<CupertinoLocalizations> {
  const FallbackCupertinoLocalisationsDelegate();

  @override
  bool isSupported(Locale locale) => true;

  @override
  Future<CupertinoLocalizations> load(Locale locale) =>
      DefaultCupertinoLocalizations.load(locale);

  @override
  bool shouldReload(FallbackCupertinoLocalisationsDelegate old) => false;
}

Ahora, desde cualquier otra clase, necesita cambiar la configuración regional, puede usar mi camino:

Importar dardo principal archivo, luego use mi simple DropdownButton:

Nota: reemplazar valor: AppLocalizations.of(context)!.locale.toString(), esta línea con su forma de obtener la configuración regional actual de la aplicación…

import 'package:yout_project_name/main.dart';

DropdownButton(
  onChanged: (v) => setState(() {
    MainApp.setLocale(context, Locale(v.toString(), ""));
  }),
  value: AppLocalizations.of(context)!.locale.toString(), // change this line with your way to get current locale to select it as default in dropdown
  items: [
    DropdownMenuItem(
      child: Text( 'English'), value: 'en'
    ),
    DropdownMenuItem(
      child: Text( 'العربية'), value: 'ar'
    ),
  ],
)

Eche un vistazo al paquete language_builder en pub.dev. Es muy fácil de usar. Al envolver su widget raíz con LanguageBuilder, puede configurar el idioma de su aplicación. Dígale a su aplicación que use el idioma de los teléfonos o cámbielo manualmente desde la aplicación.

https://pub.dev/packages/language_builder

  • Las preguntas que solicitan recursos están fuera de tema. Esta respuesta solo ofrece un enlace a un nuevo recurso y no muestra cómo implementar el recurso para resolver la pregunta en el OP. Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página enlazada cambia. – De la revisión

    –Trenton McKinney

    28 de septiembre de 2021 a las 18:06


import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MainApp());
}

class MainApp extends StatefulWidget {
  const MainApp({Key? key}) : super(key: key);

  /*
  To Change Locale of App
   */
  static void setLocale(BuildContext context, Locale newLocale) async {
    _MainAppState? state = context.findAncestorStateOfType<_MainAppState>();

    var prefs = await SharedPreferences.getInstance();
    prefs.setString('languageCode', newLocale.languageCode);
    prefs.setString('countryCode', "");

    state?.setState(() {
      state._locale = newLocale;
    });
  }

  @override
  // ignore: library_private_types_in_public_api
  _MainAppState createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
  Locale _locale = const Locale('ar', 'tn');

  @override
  void initState() {
    super.initState();
    _fetchLocale().then((locale) {
      setState(() {
        _locale = locale;
      });
    });
  }

  /*
  To get local from SharedPreferences if exists
   */
  Future<Locale> _fetchLocale() async {
    var prefs = await SharedPreferences.getInstance();

    String languageCode = prefs.getString('languageCode') ?? 'ar';
    String countryCode = prefs.getString('countryCode') ?? 'tn';

    return Locale(languageCode, countryCode);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: _locale,
      // ignore: prefer_const_literals_to_create_immutables
      localizationsDelegates: [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
        const FallbackCupertinoLocalisationsDelegate(),
      ],
      // ignore: prefer_const_literals_to_create_immutables
      supportedLocales: [
        const Locale('en', ''), // English, no country code
        const Locale('fr', ''),
        const Locale('ar', ''),
      ],
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,
      ),
      home: const AppContent(), // here use your own home name...
    );
  }
}

/*
  To solve problem of hold press on inputs
 */
class FallbackCupertinoLocalisationsDelegate
    extends LocalizationsDelegate<CupertinoLocalizations> {
  const FallbackCupertinoLocalisationsDelegate();

  @override
  bool isSupported(Locale locale) => true;

  @override
  Future<CupertinoLocalizations> load(Locale locale) =>
      DefaultCupertinoLocalizations.load(locale);

  @override
  bool shouldReload(FallbackCupertinoLocalisationsDelegate old) => false;
}

//___________________________________
class AppContent extends StatefulWidget {
  const AppContent({super.key});

  @override
  State<AppContent> createState() => _AppContentState();
}

class _AppContentState extends State<AppContent> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            Text(AppLocalizations.of(context)!.hello),
            const SizedBox(height: 10),
            TextButton(
              onPressed: () {
                setState(() {
                  MainApp.setLocale(context, const Locale("fr", ""));
                });
              },
              child: const Text("Fr"),
            ),
            const SizedBox(height: 10),
            TextButton(
              onPressed: () {
                setState(() {
                  MainApp.setLocale(context, const Locale("en", ""));
                });
              },
              child: const Text("En"),
            ),
            const SizedBox(height: 10),
            TextButton(
              onPressed: () {
                setState(() {
                  MainApp.setLocale(context, const Locale("ar", ""));
                });
              },
              child: const Text("Ar"),
            ),
            const SizedBox(height: 10)
          ],
        ),
      ),
    );
  }
}

  • Las preguntas que solicitan recursos están fuera de tema. Esta respuesta solo ofrece un enlace a un nuevo recurso y no muestra cómo implementar el recurso para resolver la pregunta en el OP. Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página enlazada cambia. – De la revisión

    –Trenton McKinney

    28 de septiembre de 2021 a las 18:06


¿Ha sido útil esta solución?