← Back to Blog

GlobalMaterialLocalizations.delegate: Complete Flutter Guide

flutterdelegatesmateriallocalizationglobalmateriallocalizationsl10n

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:

  1. Always include all three global delegates plus your app's delegate
  2. Match supportedLocales with your ARB files
  3. Use GlobalWidgetsLocalizations for proper RTL support
  4. 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.

Related Resources