flutterdartfreezedflutter-freezedjson-serializable

Using dart Freezed with sealed classes and fromJson


The documentation of freezed is telling that we should just use the sealed classes made available in dart 3. However: it does not explain how to use it, especially in combination with fromJson. So I'm a little stumped on how to do it with sealed classes instead of union types.

Given the following union;

import 'package:freezed_annotation/freezed_annotation.dart';

part 'example.freezed.dart';
part 'example.g.dart';

@freezed
sealed class Example with _$Example {
  const factory Example.text({
    required String text,
  }) = _ExampleText;

  const factory Example.nameLocation({
    required String name,
    required String location,
  }) = _ExampleNameLocation;

  factory Example.fromJson(
    Map<String, dynamic> json,
  ) =>
      _$ExampleFromJson(json);
}

void main() {
  print(Example.fromJson({'runtimeType': 'text', 'text': 'Hello'}));
  print(Example.fromJson({
    'runtimeType': 'nameLocation',
    'name': 'John',
    'location': 'Amsterdam'
  }));
}

I tried something like this - but that doesn't work. (The non-abstract class '_$ExampleTextImpl' is missing implementations for these members: _$Example.toJson)

import 'package:freezed_annotation/freezed_annotation.dart';

part 'example.freezed.dart';
part 'example.g.dart';

@freezed
sealed class Example with _$Example {
  const factory Example() = _Example;
  factory Example.fromJson(
    Map<String, dynamic> json,
  ) =>
      _$ExampleFromJson(json);
}

@freezed
class ExampleText extends Example with _$ExampleText {
  const factory ExampleText({required String text}) = _ExampleText;
}

@freezed
class ExampleNameLocation extends Example with _$ExampleNameLocation {
  const factory ExampleNameLocation({
    required String name,
    required String location,
  }) = _ExampleNameLocation;
}

void main() {
  print(Example.fromJson({'runtimeType': 'ExampleText', 'text': 'Hello'}));
  print(Example.fromJson({
    'runtimeType': 'ExampleNameLocation',
    'name': 'John',
    'location': 'Amsterdam'
  }));
}

Solution

  • Looks like it's the same as before, except we would make the classes not private and freezed will generate the classes after the =.

    import 'package:freezed_annotation/freezed_annotation.dart';
    
    part 'example.freezed.dart';
    part 'example.g.dart';
    
    @freezed
    sealed class Example with _$Example {
      const factory Example.text({
        required String text,
      }) = ExampleText;
    
      const factory Example.nameLocation({
        required String name,
        required String location,
      }) = ExampleNameLocation;
    
      factory Example.fromJson(Map<String, dynamic> json) =>
          _$ExampleFromJson(json);
    }
    
    void main() {
      final example =
          Example.fromJson({'runtimeType': 'text', 'text': 'Hello World!'});
      final type = switch (example) {
        ExampleText() => 'ExampleText',
        ExampleNameLocation() => 'ExampleNameLocation',
      };
      print(type);  // ExampleText
    }
    

    I do think this is a little unclear in the documentation, but I guess it's not that much different as before. Just don't use when.