generic_enum 0.4.3 generic_enum: ^0.4.3 copied to clipboard
Dart enumerations with extension-methods offering json-serialization and a mapping of each enum instance to a const value with arbitrary data-type.
Generic Enumerations For Dart #
Introduction #
Enumerations are ideal when we want to model choosing from a limited set of constant values.
In Dart, the value of an enum
instance resolves to a String
.
The package generic_enum
can be used together with
generic_enum_builder
to build extensions
supporting:
- mapping of enum instances to a value of arbitrary data-type,
- json-serialization.
Usage #
To use this library include generic_enum
as
dependencies in your pubspec.yaml
file.
Include generic_enum_builder
,
and build_runner
as dev_dependencies.
The example below shows how to define the enumeration DpiResolution
and map each enum instance to a value of type double
.
Click to show source code.
import 'package:generic_enum/generic_enum.dart';
// 0. Import package exception_templates.
import 'package:exception_templates/exception_templates.dart';
// 1. Add a part statement pointing to the generated file.
part 'dpi_resolution.g.dart';
// 2. Define an enumeration
// and annotate it with @GenerateJsonExtension().
@GenerateValueExtension(
valueType: double,
values: const {'90.0', '300.0', '600.0'},
)
@GenerateJsonExtension()
enum DpiResolution { low , medium, high }
The required steps are detailed below:
-
Add the import directives shown above.
-
Add a part statement referencing the generated file
dpi_resolution.g.dart
. -
Define an enumeration and annotate it with:
- @GenerateValueExtension() to generated the enum getters
value
,valueMap
andstringValue
- @GenerateJsonExtension() to generate the enum method
toJson()
andTo<EnumName>.fromJson(json)
.
- @GenerateValueExtension() to generated the enum getters
-
Configure the build targets (and amend the generate_for entry). In your local
build.yaml
file add configurations for the builders provided by the package generic_enum_builder.Click to show file content.
targets: $default: builders: # Configure the builder `pkg_name|builder_name` generic_enum_builder|extension_builder: # Only run this builder on the specified input. enabled: true generate_for: - lib/*.dart
Note: The file
dpi_resolution.dart
should be an asset that can be resolved by the builder. To limit the number of files scanned for annotationed classes during the build process one can use agenerate_for
statement in the builder configuration. -
Build the project by navigating to the project root directory and running the command:
$ dart run build_runner build --delete-conflicting-outputs
-
For the example presented here, the build process will generate the file
dpi_resolution.g.dart
.Click to show file content.
// GENERATED CODE - DO NOT MODIFY BY HAND part of 'dpi_resolution.dart'; // ************************************************************************** // ValueGenerator // ************************************************************************** /// Extension on `DpiResolution` providing value-getters. extension DpiResolutionValue on DpiResolution { /// Returns a map of type `Map<double, DpiResolution>` mapping /// each declared enum value to an instance of `DpiResolution`. double get value => const <DpiResolution, double>{ DpiResolution.low: 90.0, DpiResolution.medium: 300.0, DpiResolution.high: 600.0, }[this]!; /// Returns the String identifier of an instance of `DpiResolution`. String get stringValue => const <DpiResolution, String>{ DpiResolution.low: 'low', DpiResolution.medium: 'medium', DpiResolution.high: 'high', }[this]!; /// Returns a mapping of instance name to enum instance. Map<String, DpiResolution> get valueMap => const <String, DpiResolution>{ 'low': DpiResolution.low, 'medium': DpiResolution.medium, 'high': DpiResolution.high, }; } // ************************************************************************** // JsonGenerator // ************************************************************************** /// Extension providing the functions `fromJson()`, `toJson()`, /// and the getter `jsonEncoded`. extension ToDpiResolution on DpiResolution { /// Converts [json] to an instance of `DpiResolution`. static DpiResolution fromJson(Map<String, dynamic> json) { final index = (json['index']) as int?; if (index == null) { throw ErrorOf<DpiResolution>( message: 'Error deserializing json to DpiResolution.', invalidState: 'json[index] returned null.', expectedState: 'A map entry: {index: int value}.'); } if (index >= 0 && index < DpiResolution.values.length) { return DpiResolution.values[index]; } else { throw ErrorOf<DpiResolution>( message: 'Function fromJson could not find ' 'an instance of type DpiResolution.', invalidState: 'DpiResolution.values[$index] out of bounds.'); } } /// Converts `this` to a map `Map<String, dynamic>`. Map<String, dynamic> toJson() => {'index': DpiResolution.values.indexOf(this)}; /// Converts `this` to a json encoded `String`. String get jsonEncoded => '{"index":${DpiResolution.values.indexOf(this)}}'; }
Enum - Value Mapping #
The annotation @GenerateValueExtension
requires the following parameters:
Type valueType
: The type of the constants mapped to the enum instances.Set<String> values
. The entries are copied verbatim by the generator and must represent valid const instances of the data-typevalueType
. The number of entries must match the number of enum instances.
Limitations #
Because of this issue it is not possible to pass an instance of enum
to the function jsonEncode(Object object)
(provided by dart:convert
)
even if the function toJson()
is defined in an extension on the enum
.
Alternative ways to serialize an instance of enum are:
- Use the generated getter
toJsonEncoded
to retrieve a json encodedString
. - Pass the result of
toJson()
tojsonEncode
. - Use a full blown serialization approach like
json_serializable
. This is recommended if your project already usesjson_serializable
.
When it comes to deserialization, the usual approach is to define a factory constructor named fromJson
.
This is not possible since extensions do not support constructors. Moreover, static extension-methods
are accessed by specifying the extension name.
To keep the notation similar to the "usual approach", the extension containing the static method fromJson
is named To + Enum Name, see example below.
import 'dart:convert';
import 'package:test/test.dart';
import 'dpi_resolution.dart';
// The enum instance.
final low = DpiResolution.low;
// Encoding to Map<String, dynamic>.
// Returns: {'index': 0}
Map<String, dynamic> json = low.toJson();
// Encoding to String.
String jsonEncoded0 = jsonEncode(low); // Throws error! Extensions not available for dynamic types.
String jsonEncoded1 = jsonEncode(json) // Using dart:convert.
String jsonEncoded2 = low.jsonEncoded; // Using the generated getter.
expect(jsonEncoded1, jsonEncoded2);
// Decoding (notice the prefix "To").
expect(ToDpiResolution.fromJson(json), low);
expect(ToDpiResolution.fromJson(jsonDecode(jsonEncoded1)), low);
Examples #
Further examples on how to define and build generic enumeration classes can be found in the package generic_enum_example.
Features and bugs #
Please file feature requests and bugs at the issue tracker.