You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter实现通用HTTP CRUD方法 解决泛型序列化及请求路径配置问题

Flutter通用HTTP CRUD封装解决方案

问题一:泛型T无法调用序列化方法的解决

Dart本身不支持泛型类型的静态方法直接调度,且Flutter禁用了dart:mirrors反射能力,因此最优解决方式是通过基础接口约束+全局序列化映射实现:

  1. 首先定义所有Model的公共父接口:
abstract class BaseModel<T> {
  Map<String, dynamic> toJson();
}
  1. 所有使用json_serializable生成序列化方法的Model类实现该接口:
@JsonSerializable()
class User implements BaseModel<User> {
  final int id;
  final String name;

  User({required this.id, required this.name});

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  
  // 实现BaseModel接口的toJson方法
  @override
  Map<String, dynamic> toJson() => _$UserToJson(this);
}
  1. 在HTTP工具类中维护全局的类型->fromJson转换器映射,泛型方法中通过类型匹配获取对应转换器:
class HttpGenericClient {
  // 全局注册所有Model的反序列化方法,新增Model时只需在此处添加一条记录
  static final Map<Type, T Function<T>(Map<String, dynamic>)> _fromJsonFactories = {
    User: (json) => User.fromJson(json),
  };

  // 泛型添加BaseModel约束,确保可以调用toJson方法
  static Future<T> create<T extends BaseModel<T>>(String path, {required T data}) async {
    final parsedData = data.toJson();
    final Response<Map<String, dynamic>> response = await httpClient.post<Map<String, dynamic>>(
      path, 
      data: parsedData,
    );

    final fromJson = _fromJsonFactories[T];
    if (fromJson == null) {
      throw Exception('未找到$T类型的反序列化注册信息,请先在_fromJsonFactories中配置');
    }

    return fromJson(response.data!);
  }
}

如果项目Model数量较多,可配合自定义build_runner生成器自动完成注册逻辑,无需手动维护映射表。

问题二:请求路径绑定优化

完全可以将请求路径和Model类绑定,无需每次调用时手动传入,改造方案如下:

  1. 在BaseModel中新增接口路径的getter约束:
abstract class BaseModel<T> {
  Map<String, dynamic> toJson();
  // 新增接口路径约束,每个子类实现自身对应的接口路径
  String get apiPath;
}
  1. 在对应的Model类中实现该getter:
@JsonSerializable()
class User implements BaseModel<User> {
  // ... 原有属性、构造、序列化方法不变

  @override
  String get apiPath => '/users';
}
  1. 修改HTTP泛型方法,直接从传入的Model实例中获取路径:
static Future<T> create<T extends BaseModel<T>>({required T data}) async {
  final parsedData = data.toJson();
  // 直接从实例取路径,无需外部传入
  final Response<Map<String, dynamic>> response = await httpClient.post<Map<String, dynamic>>(
    data.apiPath, 
    data: parsedData,
  );
  // ... 原有反序列化逻辑不变
}

最终调用方式可简化为:

HttpGenericClient.create<User>(data: user);

如果涉及无需传Model实例的请求(比如查询列表、删除单条),可在HttpGenericClient中额外维护一份类型到路径的全局映射,调用时直接通过泛型类型匹配路径即可。

内容的提问来源于stack exchange,提问作者saibot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 07:09:03