@freezed copyWith missing with sealed classes

5.5k views Asked by At

In the following snippet the state.copyWith function is not available.

@freezed
class MyState with _$MyState {
  @JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
  const factory MyState({@Default(0) int counter,}) = _MyState;
  const factory MyState.initial({@Default(0) int counter}) = Initial;
  const factory MyState.loading() = Loading;
  const factory MyState.one() = One;
  const factory MyState.two() = Two;

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

class MyStateNotifier extends StateNotifier<MyState> {
  MyStateNotifier() : super(MyState.initial());

  Future<void> one() async {
    state = MyState.loading();
    await Future.delayed(Duration(seconds: 5));
    state.copyWith(counter: 1);
  }
}

However when I remove the sealed classes the copyWith function is available.

@freezed
class MyState with _$MyState {
  @JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
  const factory MyState({@Default(0) int counter,}) = _MyState;
  // const factory MyState.initial({@Default(0) int counter}) = Initial;
  // const factory MyState.loading() = Loading;
  // const factory MyState.one() = One;
  // const factory MyState.two() = Two;

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

class MyStateNotifier extends StateNotifier<MyState> {
  MyStateNotifier() : super(MyState());

  Future<void> one() async {
    await Future.delayed(Duration(seconds: 5));
    state.copyWith(counter: 1);
  }
}

What do I need to change to make the copyWith available in the first snippet?

2

There are 2 answers

4
Renato On BEST ANSWER

Only properties that are common to all constructors will generate a copyWith method, as mentioned in the README docs.

We can use copyWith with properties defined on all constructors...

Imagine you had an instance of Loading, what copyWith method would you expect that to have? It has no properties, hence it can't have any copyWith methods, therefore the union of all types also can't.

However, you can use pattern matching to call copyWith on the instances of the right type.

In your example, something like this would work:

MyState myState = ...;

myState.maybeMap(
    initial: (v: Initial) => v.copyWith(counter: v.counter + 1),
    orElse: () => ...,
);

Or using when:

MyState myState = ...;

myState.when(
    (int counter) => MyState.initial(counter + 1),
    loading: () => loading,
    one: () => one,
    two: () => two,
);
0
Marius Ruica On

you can access the copyWith method, do Type conversion:

`if (state is CurrentState) { final myState = state as CurrentState }

myState.copyWith (...)`