Guide to State Management in Flutter with GetX || Part - 2

In our previous article, we explored the basics of GetX and its core features. We discussed the introduction to GetX, demonstrated how to work with reactive state variables, explored dependency injection using GetX, and highlighted the advantages of using GetX over other state management libraries. If you missed any of these topics, please revisit the first part to build a solid foundation. Now, let’s take our understanding of GetX to the next level. In this article, we’ll delve deeper into advanced techniques, uncover hidden gems, and empower you to create robust and efficient Flutter applications using GetX.

Syntax for Defining Named Routes Using GetX

  1. First, make sure you’ve set up GetX in your app:

    • Install the get package by running:

        flutter pub add get
      
    • Import the library into your Dart code:

        import 'package:get/get.dart';
      
  2. Replace MaterialApp with GetMaterialApp in your app’s main widget tree:

     GetMaterialApp(
       // Other properties...
       getPages: [
         GetPage(name: '/page-one', page: () => PageOne()),
         GetPage(name: '/page-two', page: () => PageTwo()),
         // Add more named routes as you needed
       ],
     )
    
  3. Define your named routes using GetPage:

    • The name parameter specifies the route name (e.g., ‘/page-one’).

    • The page parameter is a callback that returns the widget for that route.

That’s it! You can now navigate to these named routes using Get.toNamed('/page-one') , Get.toNamed('/page-two') and so on.


What is internationalisation in Flutter using GetX?

Internationalisation (often abbreviated as i18n) in Flutter refers to the process of adapting your app to support multiple languages and locales. It allows you to create a single codebase that can display content in different languages based on the user’s preferences. In the Context of GetX, a popular state management library for Flutter, you can achieve internationalisation efficiently using, the Getx_Translator package.

Here are the steps to set up internationalisation using GetX_Translator:

  1. Setting Up the Google Sheet for GetX_Translator:

    • Create a Google Sheet to serve as a centralised hub for all your application’s translations.

    • Define the structure of the sheet with headers for keys (used in your Flutter app) and columns for different languages.

    • Populate the sheet with localisation data, linking keys to their respective translations.

  2. Leveraging Google App Script for Communication:

    • Use Google App Script to establish communication between the Google Sheet and your Flutter app.

    • Create a script for your sheet and paste the provided code in the script editor.

    • This script handles HTTP requests and manages the sheet’s data.

With this setup, managing translations becomes efficient and collaborative. You'll be able to dynamically switch between languages in your Flutter app based on user preferences.


Some best practices for internationalisation in Flutter:

  1. Use intl Package:

    • The intl package provides essential tools for formatting dates, numbers, and messages in different languages.

    • Leverage intl for date and number formatting, pluralisation, and message interpolation.

    • It supports various locales, so you can adapt your app to different regions seamlessly.

  2. Separate Texts from UI Widgets:

    • Avoid hardcoding strings directly into UI widgets (e.g., Text('Hello')).

    • Instead, define your strings in a separate file (e.g., strings.dart) and reference them using keys.

    • This makes it easier to replace translations and maintain consistency.

  3. Choose a Localisation Strategy:

    • Decide whether to use resource files (.arb or .json) or a centralised solution (like Google Sheets with GetX_Translator).

    • Resource files are straightforward but can become unwieldy for large apps.

    • Centralised solutions allow collaboration and dynamic updates but require additional setup.

  4. Test with Different Locales:

    • Regularly test your app with different locales to ensure text fits within UI elements.

    • Pay attention to text expansion (some languages require more space) and right-to-left (RTL) layouts.

  5. Provide Context for Translators:

    • When adding keys to your localisation files, include context information.

    • For example, instead of just "Save", use "buttonSave" or "actionSave" to give translators context.

  6. Handle Plurals and Gender Variations:

    • Different languages have varying rules for plurals and gender.

    • Use the intl package’s Intl.plural and Intl.gender methods to handle these variations.

  7. Localise Dates and Times:

    • Use DateFormat from the intl package to format dates and times according to the user’s locale.

    • Be aware of different date formats (e.g., day-month-year vs. month-day-year).

Remember that internationalisation is an ongoing process. Regularly update your translations, test thoroughly, and consider user feedback to improve the localisation experience.


How to Change theme using GetX in Flutter

  1. Create a Theme Controller:

    • First, create a controller class (let’s call it ThemeController) that manages the theme state.

    • In this controller, define a boolean property (e.g., isDarkMode) to track whether the app is in dark mode or light mode.

  2. Toggle the Theme:

    • Implement a method in ThemeController (e.g., toggleDarkMode()) that toggles the isDarkMode property.

    • Depending on the value of isDarkMode, switch between your light and dark theme data.

  3. Use GetX to Change the Theme:

    • In your UI (e.g., HomeScreen), use GetBuilder<ThemeController> to listen for changes in the theme state.

    • Inside the IconButton (for toggling the theme), update the icon based on isDarkMode.

    • When the user taps the icon, call controller.toggleDarkMode() to switch the theme.

  4. Persist the Theme State:

    • To make the theme persist across app restarts, use the get_storage package.

    • Create a helper class (e.g., ThemeHelper) that reads and writes the theme mode to local storage.

    • Save the theme mode when the user toggles it, and load it when the app starts.

Here’s a simplified example of how you can achieve this:

// ThemeController.dart
class ThemeController extends GetxController {
  bool isDarkMode = false;

  void toggleDarkMode() {
    isDarkMode = !isDarkMode;
    if (isDarkMode) {
      Get.changeTheme(themeDataDark);
    } else {
      Get.changeTheme(themeDataLight);
    }
    update();
  }
}
// main.dart
void main() async {
  await GetStorage.init();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      theme: themeDataLight,
      darkTheme: themeDataDark,
      themeMode: ThemeHelper().theme,
      home: HomeScreen(),
    );
  }
}

// ThemeHelper.dart
class ThemeHelper {
  final _box = GetStorage();
  final _key = 'isDarkMode';

  ThemeMode get theme => _loadThemeFromBox() ? ThemeMode.dark : ThemeMode.light;

  bool _loadThemeFromBox() => _box.read(_key) ?? false;

  void switchTheme() {
    Get.changeThemeMode(_loadThemeFromBox() ? ThemeMode.light : ThemeMode.dark);
    _saveThemeToBox(!_loadThemeFromBox());
  }

  void _saveThemeToBox(bool isDarkMode) => _box.write(_key, isDarkMode);
}

Remember to adjust the theme data (themeDataLight and themeDataDark) according to your need for app’s design.


Validation in Flutter using GetX

Validation in Flutter using GetX involves handling form input validation and displaying appropriate error messages. Let’s break it down:

  1. Create a Form Controller:

    • Start by creating a controller (e.g., FormController) that manages form-related state.

    • Define observables for form fields (e.g., RxString for username, email, etc.).

    • Use debounce to validate input after a delay (e.g., 500 milliseconds) to avoid excessive validation calls.

  2. Implement Validation Logic:

    • In the validations method, perform your validation checks (e.g., length, availability, etc.).

    • Update the errorText observable with validation errors or set it to null if validation passes.

    • Disable the submit button (submitFunc) during validation.

  3. UI Integration:

    • In your UI (e.g., FormObxPage), use Obx to listen for changes in observables.

    • Bind the onChanged callback of TextFormField to the corresponding controller function (e.g., usernameChanged).

    • Enable/disable the submit button based on the submitFunc observable.

Here’s a simplified example:

class FormController extends GetxController {
  RxString username = RxString('');
  RxnString errorText = RxnString(null);
  Rxn<Function()> submitFunc = Rxn<Function()>(null);

  void usernameChanged(String val) {
    username.value = val;
  }

  void validations(String val) async {
    errorText.value = null; // Reset validation errors
    submitFunc.value = null; // Disable submit while validating

    if (val.isNotEmpty) {
      if (lengthOK(val) && await available(val)) {
        print('All validations passed, now enable submit button...');
        submitFunc.value = submitFunction();
        errorText.value = null;
      } else {
        errorText.value = 'please enter valid input'; // Set appropriate error message
      }
    }
  }
  // Other validation methods (lengthOK, available, etc.) go here
}

// UI Example
class FormObxPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    FormController fx = Get.put(FormController());

    return Scaffold(
      body: Column(
        children: [
          TextFormField(
            onChanged: fx.usernameChanged,
            decoration: InputDecoration(
              labelText: 'Username',
              errorText: fx.errorText.value,
            ),
          ),
          ElevatedButton(
            child: Text('Submit'),
            onPressed: fx.submitFunc.value,
          ),
        ],
      ),
    );
  }
}

In conclusion, mastering state management in Flutter using GetX can significantly enhance the efficiency and robustness of your applications. By understanding advanced techniques such as defining named routes, implementing internationalisation, changing themes, and handling validation, you can create a seamless and dynamic user experience. Remember to follow best practices for internationalisation and persist theme states to ensure a consistent and user-friendly interface.

10
Subscribe to my newsletter

Read articles from Fenisha Koladiya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Fenisha Koladiya
Fenisha Koladiya