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;}
By default, Flutter only provides US English localizations. To add support for other languages, an application must specify additional MaterialApp (or CupertinoApp ) properties, and include a package called flutter_localizations . As of November 2020, this package supports 78 languages.
Locale myLocale = Localizations. localeOf(context); String lc = currentLanguageCode(myLocale); print ("Language code: ${lc}"); The problen is from the void initState() statement, the context is null and will then return an empty string.
Allow the app user to select his desired language to use your flutter app. To Add Localization in flutter app, We will use flutter getX Internationalization to change app language in flutter. Video Player is loading.
Giving multiple language support in any application will increase large member of audience for the application.So in Flutter Localization example we will cover how do we change the Text Language based on device Language. What is Localization in flutter, Flutter provides widgets to implmenet internationalization support for the applciations.
How to use translations in flutter with Getx You have defined your locale languages in translations class. you can use the specified ‘key’ & just append with .tr & thus the locale language will be translated in your flutter app. Note: Here ‘hello’ is the key specified in translation class above.
Looks like this tutorial/code needs updating for new Flutter changes. 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:
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')),
),
],
);
}
}
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,
assets/languages/
in the root folderJSON
files for your supported languages.
Like: en.json, es.jsonmain.dart
create default 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();
},
),
);
}
}
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'),
];
}
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;
}
}
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);
}
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
If you are using provider as state management then you can follow this article .
https://medium.com/flutter-community/flutter-internationalization-the-easy-way-using-provider-and-json-c47caa4212b2
Because accepted answer has some shortcomings I will set new one:
Change locale of App can be done in several ways, here i will show two way:
Way 1 (using rxdart
and SharedPreferences
plugins):
For more understanding i was created project to explain how do that, you can find it in https://github.com/AnasSafi/flutter_localization_example
Output of project:
Way 2:
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'
),
],
)
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.
https://pub.dev/packages/language_builder
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With