Building HashTrack with Flutter: Main class and Localization setup

If you haven’t check out the previous post in these series, then you can do so here:

You can also find the code for this guide here:

https://github.com/hitherejoe/HashTrack-Public


Now that our project is setup, we’re ready to go ahead and start building our application. In this post we’re going to setup the core classes used to launch our application, followed by adding support for localization. Throughout this series we’re only going to support a single Locale, but having this support in place from the start makes it easier for us to add more supported locales in the future.


To kick things off, we need to build the widget which is going to house our application. For this, we’re going to create a new class called app.dart this is going to be the Class which builds our application instance in the form of the MaterialApp class.

class HashTrackApp extends StatelessWidget {

  HashTrackApp();

  @override
  Widget build(BuildContext context) {
return new MaterialApp(

    );
  }
}

This class is currently going to be a little empty as we’re not going to add any implementation parts just yet as we need to actually have this class launched when our application is run. For this we’re going to need to add a new class called main.dart, this will simply just involve a call to the runApp() function which will be used to inflate our HashTrackApp widget and attach it to the screen for use.

Future<void> main() async {
  runApp(new HashtrackApp());
}

When our application is launched, the main class is first run which will then in turn display our HashTrackApp widget to the user. However at this point you won’t be able to actually run our app just yet, as the MaterialApp instance we instantiated doesn’t have any routes or a home destination defined so an exception will just be thrown. We’re not going to add any screens just yet in this series as there’s some more initial setup that we want to do here before we continue with building our app.


What we want to setup here is Localization—this will help us pave the way to supporting more locales within our application. So to begin with we need to add some dependencies which will give us a hand when it comes to this localization support. We’ll go ahead and add the following to our pub.yaml file:

flutter_localizations:
    sdk: flutter

intl:
intl_translation:

The dependencies give us access to the flutter localization tools, as well as the intl packages that are core Dart dependencies. Now that we’ve added these, we can go ahead and start adding localization support. Now that these have been added, we’re going to begin setting up the localization support.

For this we’re going to begin by hopping back over to our app.dart class and adding a collection of supported Locale definitions for the supportedLocales property that will currently be supported by our application. This property takes an array of Locales — the constructor of this class takes a language and country code. For now, we’re just going to provide the en language code as we’re not supporting different country codes so this doesn’t need to be taken into account during locale support checks.

return new MaterialApp(
  supportedLocales: const <Locale>[
const Locale('en', ''),
  ],
);

Next we need to add a value for the localizationsDelegates property — this property allows us to define a collection of delegate classes whos responsibility is to define the set of localized resources for our application. you’ll notice below that we’ve added two declarations into this array:

return new MaterialApp(
  localizationsDelegates: <LocalizationsDelegate<dynamic>>[
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,  
  ],
  supportedLocales: const <Locale>[
const Locale('en', ''),
  ],
);

These two classes come out-of-the-box with the setup that we’re using, but these alone are not enough for the localization support that we want. To change this so that we have the required classes, the first class that we are going to define a Localization class which will essentially define the different String resources that are used by our app. This class itself won’t define every Locale representation of this string, but more define what should be defined by the supported locales.

https://gist.github.com/hitherejoe/820c2df169f4566cf3386f92df939e69

Let’s break down this class a little bit. You’ll notice that here we’ll define each one of our application strings as a function that returns the Localized version of this string. Here we use the Intl class to call the message function — this function defines the string which is to be translated by our translation logic (HashTrack), and will use the name of our string (applicationTitle) to handle that translation by performing a lookup against the files which we will generate further in this article. There is also a description which can be provided to give more context to translators when it comes to your string resources, essentially acting as documentation.

String get applicationTitle => 
    Intl.message(‘HashTrack’, 
        name: ‘applicationTitle’, desc: ‘The application title’);

You’ll also notice that there is also load function that we’ve defined. We pass the current Locale into this as a parameter and from here we’ll fetch the locale country code, followed by its name and then use this to initialize the string resources of our application using the current locale.

Now, this class is essentially just a helper class to house these operations and application strings — helping us to separate that responsibility from other parts of our project. Because of this, the class by itself isn’t much use by itself. During our MaterialApp instance setup earlier we declared the use of two existing localization delegates, what we need to do now is declare our own localization delegate so that we can declare this for use also. We’ll go ahead and define one like so:

https://gist.github.com/hitherejoe/a3f96470024c19ea4f129fccb626bd6f

You’ll notice that our class extends the LocalizationDelegate class — this is required as this is what our localizationsDelegates argument requires. You’ll notice in this class that:

  • We override the isSupported() function to check the current locale against a collection of supported locales. Returning a boolean value to indicate this result.
  • We override the localizationsDelegate load() function so that we can call our our load() function which is encapsulated inside of our AppLocalizations class (the one that we previously created). Again, this will load the required Localized resources ready for use.
  • Finally, we implement the shouldReload function. This is used to declare whether for not the localized resources should be reloaded when the load() function is called, which is whenever the Localizations widget is being rebuilt. For simplicity sake we are returning false here, but this may depend on your use case.

Now that this is in place, we’re ready to go ahead and generate our locale resource template that will define all of the string resources that should be provided for each our locales.

flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/l10n/app_localizations.dart

When you run this from the root of your project you’ll notice that a new file will have appeared:

intl_messages.arb

{
  "@@last_modified": "2018-08-26T15:50:56.193749",
  "applicationTitle": "HashTrack",
  "@applicationTitle": {
    "description": "The application title",
    "type": "text",
    "placeholders": {}
  }
}

Now that we have this, we’re going to go and copy and paste the file — renaming it to en_messages.arb. If you are supporting more than one locale, than you will need to create this file for each locale that you are supporting e.g intl_es.arb, intl_fr.arb etc.

intl_en.arb

{
  "@@last_modified": "2018-08-26T15:50:56.193749",
  "applicationTitle": "HashTrack",
  "@applicationTitle": {
    "description": "The application title",
    "type": "text",
    "placeholders": {}
  }
}

Now that we’ve defined our en locale strings, we can go ahead and run the next command which will generate the classes used to handle the locale resources:

flutter pub pub run intl_translation:generate_from_arb — output-dir=lib/l10n 
 — no-use-deferred-loading lib/l10n/app_localizations.dart lib/l10n/intl_*.arb

This command will generate three new files:

  • messages_all.dart — Handles the lookup and retrieval of alllocale strings
  • messages_en.dart — Handles the retrieval of local specific strings
  • messages_messages.dart — Handles the retrieval of strings outside of the defined locals

Note: You should never edit these files manually, simply rerun the commands from above when they need to be regenerated.

Now that the required resource classes have been generated we can hop on back over to our AppLocalizations class and add the import for the initializeMessages function. This wasn’t available before as the classes we required hadn’t been generated — once this has been done, the localization for our app has been setup!

We can now return to our MaterialApp declaration and add another delegate to our localizationsDelegates property, which is the TranslationsDelegate class that we previously created:

return new MaterialApp(
  localizationsDelegates: <LocalizationsDelegate<dynamic>>[
new TranslationsDelegate(),   
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,  
  ],
  supportedLocales: const <Locale>[
const Locale('en', ''),
  ],
);

This means that our translations delegate class will now be available for use when it comes to the handling our localization in our app.

And now that we have localization available within our app, we should begin to default accessing our string resources through our localization classes. That way, when we add further localization support in future we will only need to add a new intl_locale.arb file rather than go through our app. Let’s begin this practice here by setting the title of our applicatrion using our AppLocalizations class:

return new MaterialApp(
  onGenerateTitle: (BuildContext context) =>
      AppLocalizations.of(context).applicationTitle,
  ...
);

Whilst that seemed like a little bit to get the hang of, the great thing is that this is now all setup — so we can continue creating our application knowing that localization is setup, making it easy for us to open up our application to the many! If you have any thoughts about this post, or questions on how you can setup localization then feel free to get in touch!

https://github.com/hitherejoe/HashTrack-Public