Search code examples
flutterdartriverpodfreezedflutter-freezed

How to use generics with freezed sealed union objects?


I have a Flutter class that uses Freezed to create a sealed union that represents either data or an error:

@freezed
class DataOrError<T, E> with _$DataOrError {
  const factory DataOrError.loading() = Loading;

  const factory DataOrError.data(T data) = DataOrE<T, E>;

  const factory DataOrError.error(E error) = DOrError<T, E>;

  static DataOrError<T, E> fromEither<T, E>(Either<E, T> val) {
    final result = val.fold(
        (l) => DataOrError<T, E>.error(l), (r) => DataOrError<T, E>.data(r));
    return result;
  }
}

I use riverpod so I have a riverpod StateNotifier that looks like:

class RolesNotifier
    extends StateNotifier<DataOrError<List<Role>, RoleFailure>> {
  final Ref _ref;
  StreamSubscription? sub;

  RolesNotifier(Ref ref)
      : _ref = ref,
        super(const DataOrError.loading());

  /// Will run the fetch
  void fetch() {
        // fetch roles
        state = const DataOrError.loading();
        sub = _ref.read(firebaseRoleService).getRoles().listen((event) {
          state = DataOrError.fromEither<List<Role>, RoleFailure>(event);
        });
  }

// ... this class has been shortened for simplicity.
}

final rolesProvider = StateNotifierProvider.autoDispose<RolesNotifier,
    DataOrError<List<Role>, RoleFailure>>((ref) {
  return RolesNotifier(ref);
});

When I consume this provider; however, the types for DataOrError are gone:

ref
  .read(rolesProvider)
  .when(loading: (){}, data: (d) {
  // d is dynamic type not List<Role>
        
  }, error: (e){});

For some reason both d and e are dynamic types and not List<Role> & RoleFailure respectively. Everything appears to be typed correctly so why is this not working? I'm not sure if the error is with Freezed or Riverpod. I would like to avoid type casting (i.e. d as List<Role>) because that defeats the purpose of the generics.


Solution

  • The mixin has to be defined with the same generic types Like

    class DataOrError<T, E> with _$DataOrError<T,E>
    

    Although if you did not explicitly define the mixin generics ,the runner will mimic the original class types but in that case there will be no relation between them. in our case it will be a different set of T and E that is why it will be dynamic , bottom line is that you have to tell the mixin that it is indeed the same set of types.

    enter image description here