How to manage serialize / deserialize an enum property with Dart / Flutter to Firestore?
Flutter is able to generate JSON serialization code. The tutorial you can find here. It references the package json_annotation. It contains also support for enum serialization. So all you need, is use this tool and annotate your enum values with @JsonValue
.
From the code docs:
An annotation used to specify how a enum value is serialized.
That's basically all. Let me now illustrate with a small example in code. Imagine an enum with vehicles:
import 'package:json_annotation/json_annotation.dart';enum Vehicle { @JsonValue("bike") BIKE, @JsonValue("motor-bike") MOTOR_BIKE, @JsonValue("car") CAR, @JsonValue("truck") TRUCK,}
Then you can use this enum in one of your model, for example vehilce_owner.dart
that looks like this:
import 'package:json_annotation/json_annotation.dart';part 'vehicle_owner.g.dart';@JsonSerializable()class VehicleOwner{ final String name; final Vehicle vehicle; VehicleOwner(this.name, this.vehicle); factory VehicleOwner.fromJson(Map<String, dynamic> json) => _$VehicleOwnerFromJson(json); Map<String, dynamic> toJson() => _$VehicleOwnerToJson(this);}
This is what you need to provide according to the json generation howto. Now you need to run the builder or watcher to let flutter generate the code:
flutter pub run build_runner build
Then the generated code will look like as seen below. Take a look at the _$VehicleEnumMap
that has been generated respecting your @JsonValue
annotations:
// GENERATED CODE - DO NOT MODIFY BY HANDpart of 'vehicle_owner.dart';// **************************************************************************// JsonSerializableGenerator// **************************************************************************// more generated code omitted here ....const _$VehicleEnumMap = { Vehicle.BIKE: 'bike', Vehicle.MOTOR_BIKE: 'motor-bike', Vehicle.CAR: 'car', Vehicle.TRUCK: 'truck',};
Gunter's answer is correct if a little incomplete.
JSON serializable does handle converting Enum's to and from a string, here is some example code of what is generated:
const _$HoursEnumMap = <Hours, dynamic>{ Hours.FullTime: 'FullTime', Hours.PartTime: 'PartTime', Hours.Casual: 'Casual', Hours.Contract: 'Contract', Hours.Other: 'Other'};
and in return it converts it back with this fairly obtuse function:
T _$enumDecode<T>(Map<T, dynamic> enumValues, dynamic source) { if (source == null) { throw ArgumentError('A value must be provided. Supported values: ' '${enumValues.values.join(', ')}'); } return enumValues.entries .singleWhere((e) => e.value == source, orElse: () => throw ArgumentError( '`$source` is not one of the supported values: ' '${enumValues.values.join(', ')}')) .key;}
I got so sick of this I decided to make a small package to take away the complexity, and it has come in pretty handy for me:
https://pub.dev/packages/enum_to_string
At the very least its unit tested over a copy/paste solution. Any additions or pull requests are welcome.
The way i do it is by just saving the index of the enum.
Lets say you have an enum:
enum Location { EARTH, MOON, MARS,}
and a class which holds the enum with following methods:
/// Returns a JSON like Map of this User object Map<String, dynamic> toJSON() { return { "name": this.name, "location": this.location.index, }; } /// Returns [Player] build from a map with informationen factory Player.fromJson(Map<String, dynamic> parsedJson) { return new Player( name: parsedJson['name'], location: Location.values.elementAt( parsedJson['location'], ), ); }