Flutter Enum — How to exchange values with your Backend and/or your app UI since Dart 2.6 thanks to extensions
If you are developing an application you will probably use not one, not two but many enums in your code. Even though creating a class is always possible, nothing beats the simplicity of declaring a simple enum.
The objective of this article is to give you some suggestions about how to work with enums in Dart and Flutter and especially on how you can communicate with your database on a remote server or transform an enum to more complex types for the presentation layer.
I did not write everything in one single file in order to be pedagogic on the principles of the clean architecture and separate the role of the Data, Domain and Presentation layers.
Domain Layer: enum declaration
Let’s imagine we want to create an enum to describe the sports practiced by an athlete. For the sake of simplicity we will have only two values in this example, plus a default “UNKNOWN” value. As we will later understand this is important to ensure integrity with our remote database solution.
This declaration belongs obviously to our domain layer.
The counterpart of enums declaration simplicity is that Dart sees them only as a 0 based integer list. This may pose two possible problems :
- Convert the value to a more complex type for the presentation layer.
- Store and retrieve the value from a remote database without being index dependent on the enum inner indexation.
Let’s cover them one by one.
Presentation Layer: Convert enums to complex types for your UI
Since Dart 2.6, we can use extensions to add methods to our classes, including enums ! That makes it incredibly easy to add some conversions and even effectuate complex transformations.
For instance we may want to show the enum Value as a capitalized String and even internationalize it for the user language. Or we may want to show an image, or an icon, depending on its value. Or… we may want to do both ! Nothing is more easy using extensions.
We will just need to create a new sport_enum_extension.dart file in our presentation layer which would look as follow:
now our Sport enum magically has conversion methods ready to be used in our UI:
Data layer: Exchange enums with a remote database
When storing the value to a database on our server, we have two different approach. The most straightforward approach which results in the fastest implementation client side is to store the “description” of the value as a string. This also improve readability and speed up debugging.
Although, this is easy to understand and read in the database, this is not always the most efficient solution as we will see later in the article and in some cases you may want to choose between these two approaches (if reducing overhead and storage is a key point for your application). In our example we will have to store “VOLLEYBALL”, “BEACHVOLLEY” or “UNKNOWN”. Natively enum type implements a toString() method that results in the following output:
If we only want the last bit to be stored in our Database, we could either split the resulting String on “.” and take the second part. Or… we may rely on the amazing flutter team who already wrote this for us !
In the flutter Foundation library, an helper method “describeEnum” does exactly what we are trying to achieve.
We will now get the correct output :
Great! We are now able to store the enum value in the database ! However what about retrieving it and getting back an enum instead of a String ?
In my case, the most effective way was to create two generic helper to convert any String or list of String to an Enum or list of Enum, using the firstWhere method.
This file that belongs to our Data Layer would look like this:
Note: It is important we add the “orElse” part and an UNKNOWN value to each of our enums to ensure integrity! If we happen to change the values list, without retrofitting all our database, not having a default UNKNOWN value may result into broken algorithms.
To use these two methods, we will just have to import “enum_db_helper.dart” in our repository and call the above static methods:
sport = "VOLLEYBALL";
sportList = ["VOLLEYBALL","BEACHVOLLEY"];toEnumValue(sport, values: Sport.values);
toEnumValueList(sportList, values: Sport.values);
An alternative, sometimes more efficient, approach (discouraged for most applications for which small differences in overhead and storage are not a problem).
The above solution works fine and storing the enum as String is the way to go most of the times. However, there is a more efficient solution in order to save up storage space and reduce overhead bandwidth consumption. This could be a subject in some specific applications such as TimeSeries data that would needs to store billions of lines or M2M communications that use very slow, low power, networks. In general the solution would be to just use short String that would fit in one byte as integers does, however if you have longer String, saving up 1 byte per request may still count…
Much more similarly to what is done in classic relational databases, we could store an integer value, each corresponding to the enum (ATTENTION: it has to be a code specific to each value and not their index in the enum, otherwise it would become tricky to update your enums and add or remove values).
Again extensions would do the trick in this case and allow us to have a very simple straightforward solution. The drawback in this case is that we will need to be more verbose client side and create an extension file for each of the enum declared in our app. Another drawback is the fact that in order to change the code corresponding to a value you need to retrofit your database and you may lose compatibility with older versions of your app. Also, debugging and database manual reading will be more complicated as you have to decode each value for readability. To somewhat reduce the latter you may also want to create a description table in your database in order to have the correspondence code-enum available on the remote server without having to check the code base.
In any case, I wanted to show you this alternative also because extensions would be the way to go if you would prefer to do some transformations on the data to be stored that would be specific for a given enum.
In this case we will have to create a new file in our data layer for each enum. In our example “sport_enum_db_extension.dart”:
NOTE: static methods from extensions are accessible only from the extension namespace. Which means that, in our case, we have to call SportDBX.fromDBCode(code) instead of Sport.fromDBCode(code).
Hence, to use the extension we may now just call the two conversion methods as follow:
Sport sport = "VOLLEYBALL";
int code = 1;
List<Sport> sportList = ["VOLLEYBALL","BEACHVOLLEY"];
List<int> codeList = [1,2];sport.toDBCode();
I hope this will help you in your app developments and let you save some time!
I’m just starting to write down a few tutorials, and this is one of my first posts, so please feel free to comment on it (any feedback is welcome) or support me by buying me a coffee.