Roboroads
Roboroads

Reputation: 1709

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'
  }));
}

Upvotes: 0

Views: 1424

Answers (3)

Eldar Prylutskyi
Eldar Prylutskyi

Reputation: 41

This article describes how to use sealed classes with freezed:

https://tomasrepcik.dev/blog/2024/2024-03-27-freezed-pattern-matching/

import 'package:freezed_annotation/freezed_annotation.dart';

part 'main_screen_state.freezed.dart';

// sealed in front of the main screen
@freezed
sealed class MainScreenState with _$MainScreenState {
  const MainScreenState._();

  const factory MainScreenState.init() = Init;

  const factory MainScreenState.loading() = Loading;

  const factory MainScreenState.content(List<String> data) = Content;
}

Usage:

switch (state) {
  case Init():
    // your logic for Init()
  case Loading():
    // your logic for Loading()
  case Content(): 
    // your logic for Content()
    state.data // accessing the internal data - autocast 
}

And as for fromJson it will work with runtimeType: init, loading or content for this example.

Upvotes: 1

Roboroads
Roboroads

Reputation: 1709

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.

Upvotes: 1

Volodymyr Kadomtsev
Volodymyr Kadomtsev

Reputation: 1

To use sealed classes with Freezed in Dart, you don't need to create separate classes for each variant. Instead, you can define all variants within the same sealed class and handle them using pattern matching. Here's how you can modify your code:

import 'package:freezed_annotation/freezed_annotation.dart';

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

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

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

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

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

  example1.when(
    text: (String text) => print('Text: $text'),
    nameLocation: (String name, String location) =>
        print('Name: $name, Location: $location'),
  );

  example2.when(
    text: (String text) => print('Text: $text'),
    nameLocation: (String name, String location) =>
        print('Name: $name, Location: $location'),
  );
}

Upvotes: -1

Related Questions