Building a Language Selector Dropdown Widget in Flutter
Developing applications for a global audience often requires supporting multiple languages. Flutter provides robust tools for internationalization and localization, making it relatively straightforward to implement multi-language support. A common UI element for switching languages is a dropdown selector. This article will guide you through building a professional language selector dropdown widget in Flutter, leveraging Flutter's localization features and state management.
Prerequisites
- Basic understanding of Flutter development.
- Familiarity with state management concepts (we'll use
Providerfor simplicity).
1. Setting Up Localization Infrastructure
First, we need to set up Flutter's localization system. This involves configuring pubspec.yaml, defining a l10n.yaml file, and creating ARB (Application Resource Bundle) files for our localized strings.
1.1. Configure pubspec.yaml
Add the following dependencies to your pubspec.yaml file:
dependencies:
flutter:
sdk: flutter
flutter_localizations: # Add this
sdk: flutter
provider: ^6.0.5 # For state management
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
intl: ^0.18.1 # For code generation
flutter:
uses-material-design: true
generate: true # Enable localization code generation
The flutter_localizations package provides the core localization delegates, and intl is used by Flutter's code generation for localization. generate: true under the flutter section is crucial to enable automatic generation of localization files.
1.2. Create l10n.yaml
Create a file named l10n.yaml in the root of your project (same level as pubspec.yaml) with the following content:
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 (lib/l10n), which file to use as the template (app_en.arb), and what to name the generated localization class file.
1.3. Create ARB Files
Create a folder lib/l10n. Inside this folder, create your ARB files. For example, for English and Spanish:
lib/l10n/app_en.arb:
{
"@@locale": "en",
"language": "English",
"helloWorld": "Hello World!",
"selectLanguage": "Select Language"
}
lib/l10n/app_es.arb:
{
"@@locale": "es",
"language": "Español",
"helloWorld": "¡Hola Mundo!",
"selectLanguage": "Seleccionar Idioma"
}
After saving these files, run flutter pub get and then flutter gen-l10n (or simply run your app). Flutter will automatically generate app_localizations.dart and related files in your .dart_tool directory. You can access generated strings via AppLocalizations.of(context)!.myString.
2. Managing Application Locale State
To dynamically change the application's language, we need a way to manage and expose the current Locale. We'll use ChangeNotifier and Provider for this.
lib/providers/locale_provider.dart:
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class LocaleProvider extends ChangeNotifier {
Locale? _locale;
Locale? get locale => _locale;
// List of all supported locales
static final all = AppLocalizations.supportedLocales;
void setLocale(Locale newLocale) {
if (!AppLocalizations.supportedLocales.contains(newLocale)) return;
_locale = newLocale;
notifyListeners();
}
void clearLocale() {
_locale = null;
notifyListeners();
}
}
This LocaleProvider holds the currently selected Locale and notifies listeners whenever it changes. It also exposes a list of all supportedLocales from the generated AppLocalizations.
3. Implementing the Language Selector Dropdown Widget
Now, let's create the dropdown widget itself. This widget will listen to the LocaleProvider and allow users to select a new language.
lib/widgets/language_selector_dropdown.dart:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:your_app_name/providers/locale_provider.dart'; // Adjust path
class LanguageSelectorDropdown extends StatelessWidget {
const LanguageSelectorDropdown({super.key});
@override
Widget build(BuildContext context) {
final localeProvider = Provider.of<LocaleProvider>(context);
final currentLocale = localeProvider.locale ?? Localizations.localeOf(context);
// Get the localized name for each language
String getLanguageName(Locale locale) {
switch (locale.languageCode) {
case 'en':
return AppLocalizations.of(context)!.language; // Use localized string for "English"
case 'es':
return AppLocalizations.of(context)!.language; // Use localized string for "Español"
default:
return locale.languageCode;
}
}
return DropdownButton<Locale>(
value: currentLocale,
icon: const Icon(Icons.language),
onChanged: (Locale? newLocale) {
if (newLocale != null) {
localeProvider.setLocale(newLocale);
}
},
items: LocaleProvider.all.map<DropdownMenuItem<Locale>>((Locale locale) {
return DropdownMenuItem<Locale>(
value: locale,
child: Text(getLanguageName(locale)),
);
}).toList(),
);
}
}
This widget:
- Retrieves the current
Localefrom theLocaleProvider. - Builds a
DropdownButtonwhere each item represents a supported language. - Uses
AppLocalizations.of(context)!.languageto display the localized name of the language (e.g., "English" or "Español") within the dropdown itself. - When a new language is selected, it calls
localeProvider.setLocale()to update the application's locale.
4. Integrating with MaterialApp
Finally, we need to integrate our LocaleProvider and the generated localization delegates into our MaterialApp.
lib/main.dart:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:your_app_name/providers/locale_provider.dart'; // Adjust path
import 'package:your_app_name/widgets/language_selector_dropdown.dart'; // Adjust path
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => LocaleProvider(),
builder: (context, child) {
final localeProvider = Provider.of<LocaleProvider>(context);
return MaterialApp(
title: 'Flutter Localization Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
// Set the locale based on the provider
locale: localeProvider.locale,
// Define all supported localization delegates
localizationsDelegates: const [
AppLocalizations.delegate, // Generated app localizations
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
// Define all supported locales
supportedLocales: LocaleProvider.all,
home: const MyHomePage(),
);
},
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.selectLanguage), // Localized app bar title
actions: const [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: LanguageSelectorDropdown(), // Our language selector widget
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
AppLocalizations.of(context)!.helloWorld, // Display localized greeting
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 20),
Text(
'Current Locale: ${Localizations.localeOf(context).languageCode}',
),
],
),
),
);
}
}
In main.dart:
- We wrap our
MaterialAppwith aChangeNotifierProvider<LocaleProvider>to make the provider accessible throughout the widget tree. - The
localeproperty ofMaterialAppis set tolocaleProvider.locale, ensuring the app reacts to language changes. localizationsDelegatesmust includeAppLocalizations.delegate(generated from our ARB files) along with the global material, widgets, and cupertino delegates.supportedLocalesspecifies all the languages your app supports, sourced fromLocaleProvider.all.
5. Using Localized Strings
Once everything is set up, accessing your localized strings is straightforward using AppLocalizations.of(context)!:
Text(AppLocalizations.of(context)!.helloWorld)
The ! operator assumes the context always has AppLocalizations available. If you need null safety, you can check for null or provide a fallback.
Conclusion
You have now successfully built a functional language selector dropdown widget in Flutter. This setup provides a clean, maintainable way to handle multi-language support in your application, allowing users to switch languages dynamically with immediate effect. Remember to add more ARB files for any additional languages you wish to support.