能否利用已有Freezed数据类构建Union类型?
这确实是个很实用的需求——没人想每次定义新的接口返回结果类型时,都重复写一遍ApiFailure的case,更怕后续修改ApiFailure结构时要到处找地方改。好在Freezed完全支持复用已有的数据类来构建通用的Union类型,这里给你两个最适合的方案:
方案一:创建泛型ApiResult(最推荐)
我们可以定义一个带泛型的ApiResult Union,把ApiFailure作为通用的失败case,成功case则用泛型接收任意你已经定义好的Model(比如ModelA、ModelB),这样完全不用重复关联ApiFailure:
import 'package:freezed_annotation/freezed_annotation.dart'; part 'api_result.freezed.dart'; @freezed abstract class ApiResult<T> with _$ApiResult<T> { // 直接复用已有的ApiFailure类,作为失败状态 const factory ApiResult.failure(ApiFailure failure) = ApiResultFailure<T>; // 用泛型接收任意成功模型,复用你已有的ModelA、ModelB const factory ApiResult.success(T data) = ApiResultSuccess<T>; }
用法示例
现在你可以这样定义接口结果变量:
// 对应ModelA的接口结果 final ApiResult<ModelA> apiResult1; // 对应ModelB的接口结果 final ApiResult<ModelB> apiResult2;
在Widget的builder里用fold处理时,写法和你之前用dartz的Either几乎一致:
return apiResult1.fold<Widget>( (failure) => ErrorWidget(failure), // failure就是你已定义的ApiFailure实例 (data) => SuccessWidget(data), // data就是ModelA实例 );
这个方案的核心优势就是ApiFailure只需要维护一次,后续不管是加字段还是改结构,所有用到ApiResult的地方都会自动同步,完全不用逐个修改不同的ApiResult类型。
方案二:针对特殊场景的自定义Union(可选)
如果你的业务场景里,某个接口的返回结果除了Failure和Success,还有其他特殊状态(比如Loading、Empty),也可以基于已有的数据类扩展,但依然复用ApiFailure:
@freezed abstract class SpecialApiResult with _$SpecialApiResult { // 复用已有的ApiFailure const factory SpecialApiResult.failure(ApiFailure failure) = _SpecialFailure; // 复用已有的ModelC const factory SpecialApiResult.success(ModelC data) = _SpecialSuccess; // 自定义额外状态 const factory SpecialApiResult.loading() = _SpecialLoading; }
这种方式适合有特殊状态需求的场景,但依然保证ApiFailure只被引用一次,避免重复维护。
你之前写法的问题说明
你之前尝试的const factory ApiResult1.modelA() = ModelA;其实不符合Freezed的语法——Freezed的factory如果要关联已有的类,需要把该类作为参数传入(比如上面的failure(ApiFailure failure)),而不是直接把factory指向已有的类。直接赋值会导致Freezed尝试生成一个和ModelA同名的新类,而不是复用你已有的ModelA,这也是为什么你会觉得无法复用的原因。
内容的提问来源于stack exchange,提问作者Abhilash Chandran

