Building a Multi-Language Toggle Widget in Flutter
In today's globalized world, providing a multi-language experience in your applications is no longer a luxury but a necessity. Flutter, with its robust internationalization and localization features, makes it straightforward to adapt your app to different languages and regions. This article will guide you through building a dynamic multi-language toggle widget, allowing users to switch the application's language on the fly.
1. Understanding Flutter Localization Basics
Before diving into the toggle widget, let's briefly recap Flutter's localization mechanism. Flutter uses the intl package and ARB (Application Resource Bundle) files to manage localized strings.
Key Components:
AppLocalizations: A class generated by Flutter's localization tool that provides access to your localized strings.Localizations.of(context)!: The method used to retrieve an instance ofAppLocalizationsfor the currentBuildContext.MaterialAppproperties:localizationsDelegates,supportedLocales, andlocaleare crucial for configuring localization.
2. Initial Setup for Localization
First, ensure your project is set up for localization.
Add Dependencies
Update your pubspec.yaml file:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.18.0 # Ensure intl is available for l10n.yaml configuration
flutter:
generate: true # This line is crucial for generating localization files
Create l10n.yaml
Create a file named l10n.yaml at the root of your project:
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
This configuration tells Flutter where to find your ARB files, which one is the template, and what to name the generated localization class.
Create ARB Files
Create a folder lib/l10n and add your ARB files. For English and Spanish, you'd have:
lib/l10n/app_en.arb:
{
"@@locale": "en",
"helloWorld": "Hello World",
"currentLanguage": "Current Language",
"english": "English",
"spanish": "Spanish"
}
lib/l10n/app_es.arb:
{
"@@locale": "es",
"helloWorld": "Hola Mundo",
"currentLanguage": "Idioma Actual",
"english": "Inglés",
"spanish": "Español"
}
After saving these files, run flutter pub get (or save your pubspec.yaml in VS Code/Android Studio) to generate app_localizations.dart.
3. Integrating Localization with MaterialApp
Your MaterialApp needs to be configured to use the generated localization delegates and supported locales. To allow dynamic language switching, we'll wrap MaterialApp in a StatefulWidget and manage the locale property.
lib/main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; // Generated localization file
import 'package:flutter_localizations/flutter_localizations.dart'; // For GlobalMaterialLocalizations
import 'package:your_app_name/language_toggle_widget.dart'; // Our custom widget
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State createState() => _MyAppState();
}
class _MyAppState extends State {
Locale? _locale; // Null means system locale or first supportedLocale
void _setLocale(Locale locale) {
setState(() {
_locale = locale;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Multi-Language App',
// List of all localization delegates
localizationsDelegates: const [
AppLocalizations.delegate, // Our generated AppLocalizations delegate
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
// List of all supported locales for our app
supportedLocales: AppLocalizations.supportedLocales, // Provides locales like 'en', 'es'
// The currently selected locale. Changing this will rebuild the app with the new language.
locale: _locale,
home: MyHomePage(setLocale: _setLocale),
);
}
}
class MyHomePage extends StatelessWidget {
final Function(Locale) setLocale;
const MyHomePage({super.key, required this.setLocale});
@override
Widget build(BuildContext context) {
// Access localized strings using AppLocalizations.of(context)
final appLocalizations = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(
title: Text(appLocalizations.helloWorld),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
appLocalizations.helloWorld,
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 30),
// Our language toggle widget
LanguageToggleWidget(setLocale: setLocale),
],
),
),
);
}
}
4. Building the Multi-Language Toggle Widget
Now, let's create the widget that allows users to switch languages. This widget will receive a callback function (setLocale) from its parent (_MyAppState) to update the app's locale.
lib/language_toggle_widget.dart:
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class LanguageToggleWidget extends StatelessWidget {
final Function(Locale) setLocale;
const LanguageToggleWidget({super.key, required this.setLocale});
@override
Widget build(BuildContext context) {
// Get the current locale being used by the app
final currentLocale = Localizations.localeOf(context);
final appLocalizations = AppLocalizations.of(context);
if (appLocalizations == null) {
return const SizedBox.shrink(); // Or a loading indicator
}
return Column(
children: [
Text(
'${appLocalizations.currentLanguage}: ${currentLocale.languageCode.toUpperCase()}',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: AppLocalizations.supportedLocales.map((locale) {
final isSelected = currentLocale.languageCode == locale.languageCode;
final buttonText = _getLanguageName(appLocalizations, locale.languageCode);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child: ElevatedButton(
onPressed: () {
// Call the callback to update the app's locale
setLocale(locale);
},
style: ElevatedButton.styleFrom(
backgroundColor: isSelected ? Colors.blue : Colors.grey,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
textStyle: const TextStyle(fontSize: 16),
),
child: Text(buttonText),
),
);
}).toList(),
),
],
);
}
// Helper function to get the localized name of the language
String _getLanguageName(AppLocalizations appLocalizations, String languageCode) {
switch (languageCode) {
case 'en':
return appLocalizations.english;
case 'es':
return appLocalizations.spanish;
default:
return languageCode.toUpperCase();
}
}
}
5. How It Works
- The
MyAppwidget, being aStatefulWidget, holds the_localestate. - The
_setLocalemethod in_MyAppStateupdates the_localeand callssetState, triggering a rebuild ofMaterialApp. - When
MaterialApprebuilds with a newlocale, Flutter's localization system automatically loads the corresponding ARB file, andAppLocalizations.of(context)will provide strings for the newly selected language. - The
LanguageToggleWidgetdisplays buttons for eachsupportedLocale. It determines the current language usingLocalizations.localeOf(context). - When a language button is pressed, it calls the
setLocalecallback, passing the newLocaleup to_MyAppState, completing the cycle.
Conclusion
Building a multi-language toggle widget in Flutter is a straightforward process when you understand the underlying localization mechanism. By managing the locale property of your MaterialApp via a StatefulWidget (or a state management solution like Provider, BLoC, Riverpod), you can offer users a seamless experience to switch languages instantly. This approach ensures that your application is accessible and user-friendly to a broader global audience. Remember to keep your ARB files updated with all necessary strings as your application evolves.