Button to change the language Flutter
Solution 1
You can set the locale
property of MaterialApp
with your favourite state management method. For example:
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')),
),
],
);
}
}
Solution 2
You have to use Localizations given from Flutter. You have to use custom delegate and JSON files for your supported languages.
I implemented using bloc
Steps to follow,
- Create a folder
assets/languages/
in the root folder - Create
JSON
files for your supported languages. Like: en.json, es.json - Create a key, value pairs for your strings in each file accordingly with their specific language strings
- In
main.dart
create defaultlocale, 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();
},
),
);
}
}
- Now create Languages Language models and constants
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'),
];
}
- Now Create app localization delegate
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;
}
}
- Now create blocs
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.Now create event
part of 'language_bloc.dart';
abstract class LanguageEvent {
const LanguageEvent();
}
class ToggleLanguageEvent extends LanguageEvent {
final LanguageEntity language;
ToggleLanguageEvent(this.language);
}
- Now create state
part of 'language_bloc.dart';
abstract class LanguageState {
const LanguageState();
}
class LanguageLoaded extends LanguageState {
final Locale locale;
LanguageLoaded(this.locale);
}
10.Now Create button to change languages.
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();
},
);
Also, please refer the link for clear implementation https://www.youtube.com/watch?v=W-2p3zB1z8k
Solution 3
If you are using provider as state management then you can follow this article .
Solution 4
Because accepted answer has some shortcomings I will set new one:
Note 1: I was used SharedPreferences plugin to save locale which client select.
Note 2: Replace ar and ps with your default locale ...
Full code of main.dart file:
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;
}
Now from any other class you need to change locale from it, you can use my way:
Import main.dart file, Then use my simple DropdownButton:
Note: replace value: AppLocalizations.of(context)!.locale.toString(), this line with your way to get current locale of app...
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'
),
],
)
Solution 5
Take a look at the language_builder package in pub.dev It is very easy to use. By wrapping your root widget with LanguageBuilder you can configure your app's language. Tell your app to use the phones' language or change it manually from the app.
Luthermilla Ecole
Updated on July 09, 2022Comments
-
Luthermilla Ecole almost 2 years
I am building an app in Flutter, so far I am using the Internationalization with JSON where the language of the app is based on the language that the user has as default in his phone its working pretty well, but I would like to give the user a chance to change the language without changing phone the system language settings, by only clicking in a button and then the application change the language without going through the settings.
Here is the code:
The Main:
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: () {}, ),],),),),);}}
The app_locations class:
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;}
-
shirsh shukla over 3 yearsthis may be helful for you,developerlibs.com/2019/03/…
-
Tanay Neotia over 3 yearsI would recommend looking into the Get package: pub.dev/packages/get. Its internationalization support is quite robust and easy to use, you can read more here: pub.dev/packages/get#internationalization. You can use the method described under "change locale" once your button is pressed to change the translations.
-
puspak saha over 3 yearsI would suggest this: medium.com/@puneetsethi25/…
-
-
drpawelo almost 3 yearsthis worked for me! With a small modification: I could not get it to work when my FlutterApp had a StatfulWidget (eg MyApp) as the main 'home'. So I wrapped the stateful MyApp widget into a stateless widget (eg. MyAppWrapper). The only thing that the MyAppWrapper does is it has a build method that returns MyApp().
-
Marcus almost 3 yearsbut this wouldn't survive app restarts, would it?
-
spkersten almost 3 years@Marcus It does indeed not survive app restarts. You'd have to persist the data.
-
Trenton McKinney over 2 yearsQuestions asking for resources are off-topic. This answer is only offering a link to a new resource, and doesn't show how to implement the resource to solve the question in the OP. While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
-
LearnFlutter about 2 yearsAny example that persists the changes
-
Sam Bains about 2 years@LearnFlutter you could use the Shared Preferences package and persist the data in there when the selection changes. Then to read the data, you could do this:
@override void didChangeDependencies() { SharedPreferencesHelper.getLanguageCode().then((languageCode) { setState(() { _locale = Locale(languageCode); }); }); super.didChangeDependencies(); }