Generates code for the extension on an enum.

Overview

Being able to add fields and methods to an enum.

Let's say we have the following enum:

enum MathOperator { plus, minus }

It would be handy to use the enum values such as instances of some regular Dart class.
For example,

final n1 = 1;
final n2 = 2.0;
MathOperator.values.forEach((operator) {
  print('$n1 ${operator.symbol} $n2 = ${operator.calculate(n1, n2)}');
});

An extension on the enum will help us with it.

Extensions can give us even more.
We can get a specific enum item from the instance of a Dart class, for example in such way:

final MathOperator? mathOperator = "+".toMathOperatorFromSymbol();


We can do it with collections as well:

final List<MathOperator?> mathOperators = ['+', '*'].toMathOperatorFromSymbol();


All these extensions can be generated by this package.

Usage

Install

First, add build_runner and EnumExtendable to the pubspec.yaml file:

# pubspec.yaml
dependencies:
  enum_extendable_annotation:

dev_dependencies:
  build_runner:
  enum_extendable_generator:

And install them:

# Dart
pub get

# Flutter
flutter packages get

Packages:

Create a PODO (Plain Old Data Object) class and add annotations

Next, we need to create a PODO class to describe properties of the enum.
For example,

@ExtendableEnum()
enum MathOperator { plus, minus }

@ExtendableEnumPodo()
class _MathOperatorPodo {
  final String symbol;
  final num Function(num, num) calculate;

  _MathOperatorPodo({
    required this.symbol,
    required this.calculate,
  });

  @ExtendableEnumValues()
  static final Map<MathOperator, _MathOperatorPodo> _values = {
    MathOperator.plus: _MathOperatorPodo(
      symbol: "+",
      calculate: (n1, n2) => n1 + n2,
    ),
    MathOperator.minus: _MathOperatorPodo(
      symbol: "-",
      calculate: (n1, n2) => n1 - n2,
    ),
  };
}

There are properties of an enum item that we want

  final String symbol;
  final num Function(num, num) calculate;

and a PODO object instance for each enum item in the Map<MathOperator, _MathOperatorPodo> values.

Three annotations are used here:

  • the enum annotation - @ExtendableEnum();
  • the PODO class annotation - @ExtendableEnumPodo();
  • the values map annotation - @ExtendableEnumValues().

Run the generator

  • If your package depends on Flutter:
    • flutter pub run build_runner build
  • If your package does not depend on Flutter:
    • dart pub run build_runner build

EnumExtendable will need us to both import the annotation and use the part keyword on the top of your files.

So, a file that wants to use EnumExtendable should start with:

import 'package:enum_extendable_annotation/enum_extendable_annotation.dart';

part 'example.enum_extendable.g.dart';

Note: If there is any collection field in the PODO object, one more import needs to be added (to compare collections correctly):

import 'package:collection/collection.dart';

Features

Extensions

Let's take the MathOperator enum as an example to explore available extensions.

@ExtendableEnum()
enum MathOperator { plus, minus }

@ExtendableEnumPodo()
class _MathOperatorPodo {
  final String symbol;
  final num Function(num, num) calculate;

@ExtendableEnumValues()
  static final Map<MathOperator, _MathOperatorPodo> _values = {
    ...
  };

1. Extension on enum

This package generates an extension on enum for all fields of the PODO class.

extension MathOperatorExt on MathOperator {
  String get symbol {
    _checkMathOperatorValues();
    return _MathOperatorPodo._values[this]!.symbol;
  }

  num Function(num, num) get calculate {
    _checkMathOperatorValues();
    return _MathOperatorPodo._values[this]!.calculate;
  }
}

2. Extensions on classes of the PODO fields types

If we need to get a specific enum item by the value of some field of the PODO class

final MathOperator? mathOperator = "+".toMathOperatorFromSymbol();

then we can use the generated class extension:

extension MathOperatorStringExt on String {
  MathOperator? toMathOperatorBySymbol({MathOperator? defaultValue}) {
    _checkMathOperatorValues();
    for (MathOperator element in _MathOperatorPodo._values.keys) {
      if (_MathOperatorPodo._values[element]!.symbol == this) {
        return element;
      }
    }
    return defaultValue;
  }
}

3. Extensions on Iterable

Finally, similar extensions will be generated for Iterable.

extension MathOperatorStringIterableExt on Iterable<String> {
  List<MathOperator?> toMathOperatorBySymbol({MathOperator? defaultValue}) {
    _checkMathOperatorValues();
    return List<MathOperator?>.of(
        map((e) => e.toMathOperatorBySymbol(defaultValue: defaultValue))
            .toList());
  }

  List<MathOperator> toNotNullMathOperatorBySymbol(
    MathOperator defaultValue,
  ) {
    _checkMathOperatorValues();
    return List<MathOperator>.of(map((e) =>
        e.toMathOperatorBySymbol(defaultValue: defaultValue) ??
        defaultValue).toList());
  }
}

Such extensions allow us to map an iterable to a list of the enum items:

final List<MathOperator?> mathOperators = ['+', '-'].toMathOperatorFromSymbol();

Annotations

ExtendableEnum

The enum should be annotated with ExtendableEnum.
It's a mandatory annotation.

ExtendableEnumPodo

Use 'ExtendableEnumPodo' to annotate the PODO class of your enum.' It's the second mandatory annotation.

ExtendableEnumValues

The map of values (PODO instances by enum values) should be annotated with ExtendableEnumValues.
This annotation can be omitted, if the name of this field is values.
There is one parameter bool checkCompleteness. If the value of this parameter is true an assert statement is generated to make sure that all enum items are in the values map. It can be useful when a new item is added into the enum.
The default value of checkCompleteness is true.

ExtendableEnumField

Any field of the PODO class can be annotated with ExtendableEnumField. This annotation is optional and can be used to determinate extensions nomenclature. It has two parameters:

  • bool enumExtension - set false if you don't want to generate a method for this field at the extension on enum.
    The default value is true.
  • bool classExtension - set false if you don't want to generate methods for a class (and Iterable of this class) of this field.
    The default value is true.

Live Templates

Find the live template for Android studio here

Libraries

builder