How to handle future enum values in Dart 2.17+

Viewed 153

My app is connected to a custom made peripheral and draws some values from it as ints which I want to convert to enums to streamline the app code.

As the hardware evolves overtime, it is of known fact that new values will come.

In my Flutter app, I handle known values (by the time of app writing) through enum extensions, providing an 'unknown' value as follows:


// Battery live data originator protocol
enum BatteryLiveDataProtocol {
  unknown, // 0
  rs485, // 1
  ttl, // 2
  canbus, // 3
  uart, //4
 ;
}

extension BatteryLiveDataProtocolExtension on BatteryLiveDataProtocol {
  /// Build the enum out of the int number
  static BatteryLiveDataProtocol fromNumber(int number) {
    switch (number) {
      case 1:
        return ttl;
      case 2:
        return rs485;
      case 3:
        return canbus;
      case 0:
      default:
        return unknown;
    }
  }
  /// Returns the number correpsonding to the enum
  int get number {
    switch (this) {

      case rs485:
        return 1;
      case ttl:
        return 2;
      case canbus:
        return 3;
      case unknown:
      default:
        return 0;
    }
  }

As you can see, this code support future version in a quite handily:

const futureValue = 13;
final p = BatteryLiveDataProtocol.fromNumber(futureValue);

Since the introduction of Dart 2.17 (& Flutter 3) I'd like to take advantage of the enhanced enums as follows:

// Battery live data originato protocol
enum BatteryLiveDataProtocol {
  unknown(),
  rs485(1),
  ttl(2),
  canbus(3),
  uart(4),
  ;

  const BatteryLiveDataProtocol(this.value);
  final num value;
}

But then how can I handle future enum values?

I could do that, everywhere I want to convert the int to the enum:

BatteryLiveDataProtocol p;
try {
   const futureValue = 13;
   p = BatteryLiveDataProtocol(futureValue);
} catch(e) {
   p = BatteryLiveDataProtocol.unknown;
}

But this is cumbersome, isn't it? It'll be cool to have the possibility to provide the body of const BatteryLiveDataProtocol(this.value); ourselves.

[UPDATE] Btw, not sure it was clear enough, but it is still possible to create a static function within the enhanced enum as follows:


  static BatteryLiveDataProtocol fromValue(int v) {
    BatteryLiveDataProtocol p;
    try {
      p = BatteryLiveDataProtocol[v];
    } catch (e) {
      p = unknown;
    }
    return p;
  }

And use it as follows:

const futureValue = 13;
final p = BatteryLiveDataProtocol.fromValue(futureValue);

// Makes p being set to "unknown".

So my question #1: is there a better/clever (aka handy) way to handle future values?

Question #2: Others language handle future enum (in Swift for example): is there a way to generate unknown(actualValue) as a dynamic enum value?

2 Answers

So my question #1: is there a better/clever (aka handy) way to handle future values?

I think you can improve your enum by moving the definition of fromNumber and number into the enum itself rather than as an extension.

Additionally I think you can improve the implementations of both fromNumber and number.

For number, enums already have a getter called index which returns the same value as what you want, so you can simply return the index in your number method.

For fromNumber we can turn it into a factory constructor, and since all enums have a static values list (that contains all enum values in the specific order they are defined), we can get the value we want by indexing into the values list. And for any invalid index we would return unknown.

The benefit of these changes to number and fromNumber is that you will no longer need to update these methods when you add another enum value.

The updated enum is as follows:

enum BatteryLiveDataProtocol {
  unknown, // 0
  rs485, // 1
  ttl, // 2
  canbus, // 3
  uart, //4
  ;

  factory BatteryLiveDataProtocol.fromNumber(int number) =>
      number >= values.length || number < 0 ? unknown : values[number];

  int get number => index;
}

Question #2: Others language handle future enum (in Swift for example): is there a way to generate unknown(actualValue) as a dynamic enum value?

No, dart's enhanced enums do not work like swift enums, enums in dart are closer to java enums. Swift calls this feature associated values, but dart enums do not have this feature.

Thanks to @mmcdon20 for the factory proposal (and btw, I couldn't replace static by factory in the code below).

But one want to make sure one handle any values: values and index may be different as in the modified example I propose below where the value of unknown is now set to -1 (index is 0).

So the code has to take care of it:

/// Battery live data originato protocol
enum BatteryLiveDataProtocol {
  unknown(-1),
  rs485(1),
  ttl(2),
  canbus(3),
  uart(4),
  ;

  const BatteryLiveDataProtocol(this.value);
  final num value;

  factory BatteryLiveDataProtocol.fromValue(int v) =>
      values.firstWhere((x) => x.value == v, orElse: () => unknown);
}
Related