You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TL;DR: Enum value options extensions are generated as global variables instead of being attached to each enum value.
Background
I’m creating a Flutter widget CupertinoSelectFormFieldRow<T> (inspired by CupertinoTextFormFieldRow and CupertinoListTile that lets the user select an option from a drop-down list or a pop-up menu, with each item corresponding to a value of an enum type. I would like to add a human-friendly name to each enum to be shown in a drop-down list or a pop-up menu (related to #919).
I found three solutions, with the first using JSON and enhanced enums in Dart, and the rest two using enums in Protocol Buffers to model the underlying data.
Solution 1: JSON + enhance enums + extra field
In the first solution, I modeled my underlying data with JSON (so that the data can be easily serialized and deserialized) and used enhanced enums in Dart, adding a title property to each enum type, and overriding the toString() method, delegating it to the title property, as shown below:
In the code above, I used candidate.toString() instead of candidate.title or candidate.name. There are two reasons:
title is not an intrinsic property of Enum or T,
The name property of an enum value contains the lowerCamelCase variable name (up, down, left, right, mobile, home, or work), which is usually different from the human-friendly name.
The problem of this solution is that it’s difficult to evolve the schema of the data model with JSON.
Solution 2: Protocol Buffers enums + one extra map for each enum type
In the second solution, I modeled my data with Protocol Buffers, representing the choices as protobuf enums, as shown below:
but there is no easy way to add a human-friendly name to each enum value. So I have to manually define two maps like this:
// In main.dartfinal directions = {
Direction.DIRECTION_UP:'UP',
Direction.DIRECTION_DOWN:'DOWN',
Direction.DIRECTION_LEFT:'LEFT',
Direction.DIRECTION_RIGHT:'RIGHT',
};
final phoneTypes = {
PhoneType.PHONE_TYPE_UNSPECIFIED:'Unspecified',
PhoneType.PHONE_TYPE_MOBILE:'Mobile',
PhoneType.PHONE_TYPE_HOME:'Home',
PhoneType.PHONE_TYPE_WORK:'Work',
};
and then add a map argument of type Map<T, String> to the CupertinoSelectFormFieldRow() constructor. The diff of main.dart against solution 1 is shown below:
The definition of the maps are separated into .proto file and .dart file, making it difficult to keep them in sync.
It requires an extra map argument for CupertinoSelectFormFieldRow(), which is inconvenient and inelegant.
Solution 3: Protocol Buffers enums + enum value options
Finally I came across a post about Alternate names for enumeration values on the Google Groups forum and I thought it’s the best solution. The post says that we can extend google.protobuf.EnumValueOptions and add any extra fields to it, like this:
It looks great, but when I applied this technique to the Dart code, the protoc Dart plugin generates one enum descriptor blob as a global variable for each protobuf enum type, with each global variable containing all the values of all the extra fields of an enum type, as shown below:
Since the values of the extra field of the enum value options are stored in separate global variables instead of being attached to each enum value. There is no way in the implementation code of CupertinoSelectFormFieldRow to retrieve the associated title field for a generic enum value. Although we eliminated the maps, an extra enumDescriptor argument has to be added to CupertinoSelectFormFieldRow. The diff of main.dart against solution 2 is shown below:
In conclusion, solution 3 has the benefit of keeping the definition of human-friendly names next to the definition of the enum values, thus making it easy to keep them in sync, but it also requires an extra enumDescriptor argument (when applying the enum value options technique to Dart), so it’s still not elegant.
My Questions
Is it possible to generate the enum value options extension in a way that it is associated with each enum value (similar to that in C++ and Java) so we can directly retrieve the extension field from the enum value instead of relying on an external map?
Why did the team decide to generate the descriptors as global variables?
TL;DR: Enum value options extensions are generated as global variables instead of being attached to each enum value.
Background
I’m creating a Flutter widget
CupertinoSelectFormFieldRow<T>
(inspired by CupertinoTextFormFieldRow and CupertinoListTile that lets the user select an option from a drop-down list or a pop-up menu, with each item corresponding to a value of an enum type. I would like to add a human-friendly name to each enum to be shown in a drop-down list or a pop-up menu (related to #919).I found three solutions, with the first using JSON and enhanced enums in Dart, and the rest two using enums in Protocol Buffers to model the underlying data.
Solution 1: JSON + enhance enums + extra field
In the first solution, I modeled my underlying data with JSON (so that the data can be easily serialized and deserialized) and used enhanced enums in Dart, adding a
title
property to each enum type, and overriding thetoString()
method, delegating it to thetitle
property, as shown below:The complete Flutter code is in this dartpad and the code for
CupertinoSelectFormFieldRow
is shown below:In the code above, I used
candidate.toString()
instead ofcandidate.title
orcandidate.name
. There are two reasons:title
is not an intrinsic property ofEnum
orT
,name
property of an enum value contains thelowerCamelCase
variable name (up
,down
,left
,right
,mobile
,home
, orwork
), which is usually different from the human-friendly name.The problem of this solution is that it’s difficult to evolve the schema of the data model with JSON.
Solution 2: Protocol Buffers enums + one extra map for each enum type
In the second solution, I modeled my data with Protocol Buffers, representing the choices as protobuf enums, as shown below:
but there is no easy way to add a human-friendly name to each enum value. So I have to manually define two maps like this:
and then add a
map
argument of typeMap<T, String>
to theCupertinoSelectFormFieldRow()
constructor. The diff ofmain.dart
against solution 1 is shown below:Toggle code
There are two problems of this solution:
.proto
file and.dart
file, making it difficult to keep them in sync.map
argument forCupertinoSelectFormFieldRow()
, which is inconvenient and inelegant.Solution 3: Protocol Buffers enums + enum value options
Finally I came across a post about Alternate names for enumeration values on the Google Groups forum and I thought it’s the best solution. The post says that we can extend
google.protobuf.EnumValueOptions
and add any extra fields to it, like this:Then in the Java code we can retrieve the extra
title
field for any enum value, like this:It looks great, but when I applied this technique to the Dart code, the
protoc
Dart plugin generates one enum descriptor blob as a global variable for each protobuf enum type, with each global variable containing all the values of all the extra fields of an enum type, as shown below:Toggle code
Since the values of the extra field of the enum value options are stored in separate global variables instead of being attached to each enum value. There is no way in the implementation code of
CupertinoSelectFormFieldRow
to retrieve the associatedtitle
field for a generic enum value. Although we eliminated the maps, an extraenumDescriptor
argument has to be added toCupertinoSelectFormFieldRow
. The diff ofmain.dart
against solution 2 is shown below:Toggle code
In conclusion, solution 3 has the benefit of keeping the definition of human-friendly names next to the definition of the enum values, thus making it easy to keep them in sync, but it also requires an extra
enumDescriptor
argument (when applying the enum value options technique to Dart), so it’s still not elegant.My Questions
References
The text was updated successfully, but these errors were encountered: