Flutter Localization Made Easy with GetX | Multilanguage Support Tutorial





Hey there, Flutter fam! 👋
 
Welcome back to another exciting adventure in the world of Flutter! Are you looking to make your Flutter app multilingual(i18n)? Today, we're diving into something that can truly take your app global: Multilanguage Support using GetX. 🌍

If you've ever thought: 
"How can I make my Flutter app support more languages without jumping through hoops?" 

Whether you're targeting global users, improving accessibility, or just want to offer a localized user experience, you are in the right place. Grab your coffee☕  --this blog post is for you!

Why bother with Multilanguage Support?

Picture this:
You build an amazing app. Clean UI, slick animations, and top-notch performance. But... it only supports English. 😬That means you're unintentionally ignoring a massive chunk of the global audience!

Adding multilanguage support: 
  • Increases your user base📈
  • Improves accessibility
  • Delivers a personalized experience🌎
And the best part?
GetX makes it smooth --no app restart, no drama. Let's get multilingual!

Step 1: Setting Up the Main File (main.dart)

Here, we set the stage. We're going to initialize our dependencies(especially for languages) and set up GetMaterialApp to handle our multilingual magic.


Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await GetStorage.init();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

  await FirebaseAppCheck.instance.activate(
    androidProvider: AndroidProvider.playIntegrity,
    appleProvider: AppleProvider.appAttestWithDeviceCheckFallback,
  );

  Map<String, Map<String, String>>? languages = await di.init();

  runApp(MyApp(languages: languages));
}

Now for the MyApp widget

class MyApp extends StatelessWidget {
  final Map<String, Map<String, String>>? languages;

  const MyApp({super.key, this.languages});

  @override
  Widget build(BuildContext context) {
    return _buildApp(context);
  }

  Widget _buildApp(BuildContext context) {
    return GetBuilder<LocalizationController>(
      builder: (localizeController) {
        return GetMaterialApp(
          locale: localizeController.locale, // Fixed typo
          translations: Message(
            languages: languages,
          ), // Assuming Message handles translations
        );
      },
    );
  }
}


Step 2: Initializing dependencies (init.dart)

This is where we load the user's saved language from SharedPreferences and lazily inject services like API clients and language repositories. Why lazyPut? It ensures the object is only created when it is needed, saving resources and speeding up app startup.


Future<Map<String, Map<String, String>>?> init() async {
  final sharedPreferences = await SharedPreferences.getInstance();
  
  Get.lazyPut(() => ApiClient());
  Get.lazyPut(() => sharedPreferences);
  Get.lazyPut(() => LanguageRepo());

  Get.lazyPut(() => LocalizationController(
      sharedPreferences: Get.find(), languageRepo: Get.find()));

  Map<String, Map<String, String>>? mappedJson =
      await Get.find<LocalizationController>().getLanguages();
  return mappedJson;
}


Step 3: Fetching Translations with fallback (getLanguages())

In this step, we try to fetch the translation file from Firebase(dynamic, updatable content), and if that fails(e.g,. no internet), we gracefully fall back to a local asset that we will be saving inside the assets folder.

Map<String, Map<String, String>>? mappedJson = {};
Future<Map<String, Map<String, String>>?> getLanguages() async {
    try {
      final http.Response response = await languageRepo.getLanguages();
      if (response.statusCode == 200) {
        var responseBody = json.decode(
          utf8.decode(
            response.bodyBytes,
          ),
        );
        responseBody.forEach(
          (
            key,
            value,
          ) {
            if (value is Map) {
              mappedJson?[key] = value.map(
                (innerKey, innerValue) {
                  return MapEntry(
                    innerKey,
                    innerValue.toString(),
                  );
                },
              );
            }
          },
        );
        return mappedJson;
      } else {
        return null;
      }
    } catch (e) {
      final context = Get.context;
      if (context != null) {
        final String data = await DefaultAssetBundle.of(
          context,
        ).loadString(
          'assets/language/translation.json',
        );
        mappedJson = json.decode(
          data,
        );
      }
      return mappedJson;
    }
  }


Step 4: The Magical .tr Method (text.tr)

This is where it gets fun. To make a string translatable, just wrap it with .tr, that's it.
Text("hello_world".tr)

Sample Translation file(translation.json)

Place this file in your assets/language/ directory. And don't forget to declare it in your pubspec.yaml

{
  "en_US": {
    "hello_world": "Hello World"
  },
  "ar_SA": {
    "hellow_world": "مرحبا بالعالم"
  }
}

Notice how each locale has a unique key (en_US, ar_SA) and inside that, a bunch of key-value translation pairs.

By following this guide, you've just enabled your Flutter localization with supported offline fallback. Now, your Flutter app is ready for the world.

💬 Loved This Guide?

  • ⭐ Bookmark it!
  • 🔁 Share it with your dev squad.
  • 💬 Comment below if you need help with dynamic translation loading or auto language detection.

Previous Post Next Post