generic_enum 0.4.3 copy "generic_enum: ^0.4.3" to clipboard
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().
   valueType: double,
   values: const {'90.0', '300.0', '600.0'},
 enum DpiResolution { low , medium, high }

The required steps are detailed below:

  1. Add the import directives shown above.

  2. Add a part statement referencing the generated file dpi_resolution.g.dart.

  3. Define an enumeration and annotate it with:

    • @GenerateValueExtension() to generated the enum getters value, valueMap and stringValue
    • @GenerateJsonExtension() to generate the enum method toJson() and To<EnumName>.fromJson(json).
  4. 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.
            # Configure the builder `pkg_name|builder_name`
              # Only run this builder on the specified input.
              enabled: true
                - 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 a generate_for statement in the builder configuration.

  5. Build the project by navigating to the project root directory and running the command:

    $ dart run build_runner build --delete-conflicting-outputs
  6. For the example presented here, the build process will generate the file dpi_resolution.g.dart.

    Click to show file content.
     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,
       /// Returns the String identifier of an instance of `DpiResolution`.
       String get stringValue => const <DpiResolution, String>{
             DpiResolution.low: 'low',
             DpiResolution.medium: 'medium',
             DpiResolution.high: 'high',
       /// 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-type valueType. 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 encoded String.
  • Pass the result of toJson() to jsonEncode.
  • Use a full blown serialization approach like json_serializable. This is recommended if your project already uses json_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.

pub points



Dart enumerations with extension-methods offering json-serialization and a mapping of each enum instance to a const value with arbitrary data-type.

Repository (GitHub)
View/report issues


API reference


BSD-3-Clause (LICENSE)


Packages that depend on generic_enum