GlobalMaterialLocalizations.delegate: Complete Flutter Guide
Understanding GlobalMaterialLocalizations.delegate is essential for proper Flutter localization. This guide explains what it does, why you need it, and how to configure it correctly.
What is GlobalMaterialLocalizations.delegate?
GlobalMaterialLocalizations.delegate is a localization delegate that provides translated strings for Flutter's Material widgets. Without it, Material widgets like DatePicker, TimePicker, AlertDialog, and SearchBar won't display localized text.
import 'package:flutter_localizations/flutter_localizations.dart';
MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate, // Material widgets
GlobalWidgetsLocalizations.delegate, // Basic widgets
GlobalCupertinoLocalizations.delegate, // iOS widgets
AppLocalizations.delegate, // Your app translations
],
supportedLocales: [
Locale('en'),
Locale('es'),
Locale('fr'),
],
);
The Three Core Delegates Explained
1. GlobalMaterialLocalizations.delegate
Provides translations for Material Design widgets:
| Widget | Translated Elements |
|---|---|
| DatePicker | Month names, day names, "Cancel", "OK" |
| TimePicker | "AM", "PM", "Hour", "Minute" |
| AlertDialog | Default button text |
| SearchBar | "Search" hint |
| Pagination | "Page X of Y", "Next", "Previous" |
| TextField | "Paste", "Copy", "Cut", "Select All" |
| AboutDialog | "View licenses", "Close" |
Example of what it translates:
// Without GlobalMaterialLocalizations:
showDatePicker(context: context, ...);
// Shows: "January", "February", "Cancel", "OK" (always English)
// With GlobalMaterialLocalizations for Spanish:
// Shows: "Enero", "Febrero", "Cancelar", "Aceptar"
2. GlobalWidgetsLocalizations.delegate
Handles fundamental widget behaviors:
- Text direction (LTR vs RTL)
- Text selection handles and behavior
- Keyboard navigation patterns
// This delegate makes RTL work correctly
Directionality.of(context); // Returns TextDirection.rtl for Arabic
3. GlobalCupertinoLocalizations.delegate
Provides translations for Cupertino (iOS-style) widgets:
| Widget | Translated Elements |
|---|---|
| CupertinoDatePicker | Date/time labels |
| CupertinoActionSheet | Button text |
| CupertinoAlertDialog | Default button text |
| CupertinoSearchTextField | "Search" placeholder |
Setting Up Delegates Correctly
Basic Setup
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
// Required: All four delegates
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
// Required: List of supported locales
supportedLocales: AppLocalizations.supportedLocales,
home: HomePage(),
);
}
}
Adding the Package
First, add the dependency:
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_localizations: # Add this
sdk: flutter
Then import it:
import 'package:flutter_localizations/flutter_localizations.dart';
Understanding Delegate Resolution
When Flutter needs a localized string, it queries delegates in order:
// Resolution order (first match wins):
localizationsDelegates: [
AppLocalizations.delegate, // 1. Check app translations first
GlobalMaterialLocalizations.delegate, // 2. Then Material translations
GlobalWidgetsLocalizations.delegate, // 3. Then widget basics
GlobalCupertinoLocalizations.delegate, // 4. Then Cupertino translations
],
How Delegates Work Internally
// Simplified delegate structure
class GlobalMaterialLocalizations extends MaterialLocalizations {
// Date picker strings
@override
String get cancelButtonLabel => 'Cancel';
@override
String get okButtonLabel => 'OK';
// Month names
@override
List<String> get narrowWeekdays => ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
// And 100+ more translated strings...
}
Locale Resolution Logic
Flutter uses this algorithm to select a locale:
MaterialApp(
// Called when system locale changes
localeResolutionCallback: (deviceLocale, supportedLocales) {
// 1. Try exact match (en_US == en_US)
for (final locale in supportedLocales) {
if (locale == deviceLocale) return locale;
}
// 2. Try language match (en_US matches en)
for (final locale in supportedLocales) {
if (locale.languageCode == deviceLocale?.languageCode) {
return locale;
}
}
// 3. Fall back to first supported locale
return supportedLocales.first;
},
);
Custom Locale Resolution
MaterialApp(
localeResolutionCallback: (deviceLocale, supportedLocales) {
// Custom logic: Prefer regional variants
if (deviceLocale != null) {
// Look for exact match first (es_MX, es_ES)
for (final locale in supportedLocales) {
if (locale.languageCode == deviceLocale.languageCode &&
locale.countryCode == deviceLocale.countryCode) {
return locale;
}
}
// Fall back to language only
for (final locale in supportedLocales) {
if (locale.languageCode == deviceLocale.languageCode) {
return locale;
}
}
}
return const Locale('en'); // Default
},
);
Creating Custom Delegates
For advanced use cases, create custom delegates:
Custom Material Localizations
class CustomMaterialLocalizations extends DefaultMaterialLocalizations {
const CustomMaterialLocalizations();
// Override specific strings
@override
String get cancelButtonLabel => 'Abort';
@override
String get okButtonLabel => 'Confirm';
@override
String get searchFieldLabel => 'Find...';
// Custom date formats
@override
String formatYear(DateTime date) {
return '${date.year} AD';
}
// Delegate factory
static const LocalizationsDelegate<MaterialLocalizations> delegate =
_CustomMaterialLocalizationsDelegate();
}
class _CustomMaterialLocalizationsDelegate
extends LocalizationsDelegate<MaterialLocalizations> {
const _CustomMaterialLocalizationsDelegate();
@override
bool isSupported(Locale locale) => true;
@override
Future<MaterialLocalizations> load(Locale locale) async {
return const CustomMaterialLocalizations();
}
@override
bool shouldReload(_CustomMaterialLocalizationsDelegate old) => false;
}
Using Custom Delegate
MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate,
CustomMaterialLocalizations.delegate, // Your custom delegate
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
);
Supported Locales
GlobalMaterialLocalizations supports 80+ locales out of the box:
// Check if a locale is supported
bool isSupported = GlobalMaterialLocalizations.delegate.isSupported(
const Locale('ja'), // Japanese
);
// All supported locales (partial list):
// af, am, ar, as, az, be, bg, bn, bs, ca, cs, da, de, el, en, es, et,
// eu, fa, fi, fil, fr, gl, gsw, gu, he, hi, hr, hu, hy, id, is, it,
// ja, ka, kk, km, kn, ko, ky, lo, lt, lv, mk, ml, mn, mr, ms, my,
// nb, ne, nl, or, pa, pl, pt, ro, ru, si, sk, sl, sq, sr, sv, sw,
// ta, te, th, tl, tr, uk, ur, uz, vi, zh, zu
Common Issues and Solutions
Issue 1: "A MaterialLocalizations was not found"
════════ Exception caught by widgets ════════
The following assertion was thrown building DatePicker:
No MaterialLocalizations found.
Solution: Add the delegate:
MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate, // Add this
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
);
Issue 2: Widgets Show English Despite Setting Locale
// Problem: DatePicker shows English months
MaterialApp(
locale: const Locale('es'), // Spanish
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
],
supportedLocales: [
const Locale('en'),
// Missing Spanish!
],
);
// Solution: Add to supportedLocales
supportedLocales: [
const Locale('en'),
const Locale('es'), // Add this
],
Issue 3: RTL Not Working
// Problem: Arabic text but LTR layout
MaterialApp(
locale: const Locale('ar'),
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
// Missing GlobalWidgetsLocalizations!
],
);
// Solution: Add GlobalWidgetsLocalizations
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate, // Handles text direction
GlobalCupertinoLocalizations.delegate,
],
Issue 4: Cupertino Widgets Not Translated
// Problem: CupertinoDatePicker shows English
CupertinoApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
// Missing GlobalCupertinoLocalizations!
],
);
// Solution: Add Cupertino delegate
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate, // Add this
],
Accessing Material Localizations Programmatically
class LocalizationHelper {
static MaterialLocalizations of(BuildContext context) {
return MaterialLocalizations.of(context);
}
static String getOkLabel(BuildContext context) {
return MaterialLocalizations.of(context).okButtonLabel;
}
static String getCancelLabel(BuildContext context) {
return MaterialLocalizations.of(context).cancelButtonLabel;
}
static String formatDate(BuildContext context, DateTime date) {
return MaterialLocalizations.of(context).formatFullDate(date);
}
static String formatTimeOfDay(BuildContext context, TimeOfDay time) {
return MaterialLocalizations.of(context).formatTimeOfDay(time);
}
}
// Usage
Text(LocalizationHelper.formatDate(context, DateTime.now()));
// Output: "Wednesday, December 25, 2025" (or localized equivalent)
Best Practices
1. Always Include All Core Delegates
// ✅ Complete delegate list
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
2. Match Supported Locales
// ✅ Keep delegates and supported locales in sync
supportedLocales: [
const Locale('en'),
const Locale('es'),
const Locale('ar'),
],
// Ensure your ARB files cover these same locales:
// lib/l10n/app_en.arb
// lib/l10n/app_es.arb
// lib/l10n/app_ar.arb
3. Test Material Widgets
testWidgets('DatePicker shows localized text', (tester) async {
await tester.pumpWidget(
MaterialApp(
locale: const Locale('es'),
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [const Locale('es')],
home: Builder(
builder: (context) {
return ElevatedButton(
onPressed: () => showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2030),
),
child: const Text('Show Picker'),
);
},
),
),
);
await tester.tap(find.text('Show Picker'));
await tester.pumpAndSettle();
// Verify Spanish text
expect(find.text('Cancelar'), findsOneWidget);
expect(find.text('Aceptar'), findsOneWidget);
});
Conclusion
GlobalMaterialLocalizations.delegate is the key to localizing Flutter's built-in Material widgets. Remember:
- Always include all three global delegates plus your app's delegate
- Match supportedLocales with your ARB files
- Use GlobalWidgetsLocalizations for proper RTL support
- Add GlobalCupertinoLocalizations if using any iOS-style widgets
With proper delegate configuration, your Flutter app's pickers, dialogs, and buttons will automatically display in the user's preferred language.