flutterdartfreezedflutter-freezed

Use Freezed union types with some implemented fields


I'm trying to use Freezed "Mixins and Interfaces for individual classes for union types" by following the documentation, but I'm struggling with one thing.

The doc examples are pretty simples and the interfaces do not define any fields. However in my case they do define some basic fields which I want my individual classes to define, at least partially.

Say I have this code :

abstract class NetworkException {
  int get code;
  String get message;
}

abstract class TechnicalException {
  String get message;
  Object? get stacktrace;
}

@freezed
class MyAppException with _$MyAppException {
  @With<NetworkException>()
  const factory MyAppException.notFound({
    // Force it to 404
    int code,
    String message,
  }) = NotFoundException;

  @With<TechnicalException>()
  const factory MyAppException.internalThing({
    // Force to "this is an internal thing exception"
    String message,
    Object? get stacktrace,
  }) = InternalThingException;

  // ... some other exceptions below
}

Any suggestions ?

If I do that (adding a required to some fields) it will work but what I would like is for my individual classes to be able to force the content of a field. As an example my NotFoundException should be able to specify that code = 404, but the only thing I've managed to do is to declared this nullable and add it a @Default(404) annotation, but that's not really what I want because this would still open the ability for callers of this class to override the code factory parameter, which I do not want.

Am I going wrong about this ? It feels like freezed is not suited for my usecase and that I am trying to twist something. I could do it without freezed but I still wanted to benefit from things like pattern matching with closed types, to be able to do :

final exception = methodThatThrows();
exception.when(
  (notFound) => something()),
  (internalThing) => somethingElse())
);

Solution

  • as you told, this is not the usecase for freezed. But if you want to go with it for some reason, here is the solution for you.

    abstract class NetworkException {
      int get code;
      String get message;
    }
    
    abstract class TechnicalException {
      String get message;
      Object? get stacktrace;
    }
    
    abstract class _NotFoundException extends NetworkException {
      @override
      int get code => 404;
    }
    
    abstract class _InternalThingException extends TechnicalException {
      @override
      String get message => 'this is an internal thing exception';
    }
    
    @freezed
    class MyAppException with _$MyAppException {
      @Implements<_NotFoundException>()
      const factory MyAppException.notFound({
        required String message,
      }) = NotFoundException;
    
      @Implements<_InternalThingException>()
      const factory MyAppException.internalThing({
        Object? stacktrace,
      }) = InternalThingException;
    }
    

    If you need further help, leave a comment.

    Hope it helps! Happy coding:)