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

如何在Dart中动态设置对象属性?基于Freezed可变模型实现

问题与解决方案

目标

实现一个能给上述创建的对象设置值的方法,调用方式如下:

setValueToUser(userPropertyName, value);

已知条件

  • 使用Freezed创建的可变模型:
import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@unfreezed
class User with _$User {
  factory User({
    required int id,
    required String email,
    required String password,
    required DateTime birthday,
  }) = _User;

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

已尝试方案

switch-case语句

初步实现了这个方法,但实际的User模型有109个属性,写这种代码太繁琐:

User setValueToUser(String propertyName, dynamic value){
   final user = User();
   switch (propertyName) {
      case 'id': 
         user.id = value as int;
         break
      case 'email': 
         user.email = value as String;
         break
      case 'password': 
         user.password = value as String;
         break
      case 'birthday': 
         user.birthday = value as DateTime;
         break
   }
   return user;
}

Map方案

尝试用Map映射属性,但Dart要求Map初始化时必须给所有字段赋值,没法只设置指定属性,没达到目的:

User setValueToUser(String propertyName, dynamic value) {
   final user = User();
   final userDictionary =  <String, dynamic>{
      'id': user.id = value as int,
      'email': user.email = value as String,      
      'password': user.password = value as String,
      'birthday': user.birthday = value as DateTime,
   };
   userDictionary[propertyName];   // 我本来想一次只给一个特定属性赋值,但这行代码对Dart来说完全没用:(
   return user;
}

需求补充

知道用字符串指定属性名不合理,希望能避免这种方式,欢迎提供相关解决方案(但给所有属性创建枚举也挺繁琐)。


解决方案

1. 类型安全的函数式调用(推荐)

放弃字符串指定属性的方式,改用函数式的属性选择,既类型安全又不用写重复代码:

// 定义通用设置方法
User updateUserProperty(User user, void Function(User) update) {
  update(user);
  return user;
}

// 调用示例
var user = User(id: 1, email: 'old@test.com', password: '123', birthday: DateTime(2000));
// 修改id
user = updateUserProperty(user, (u) => u..id = 100);
// 修改邮箱
user = updateUserProperty(user, (u) => u..email = 'new@test.com');

这种方式利用了Dart的级联操作符,完全类型安全,IDE还能自动补全属性,不用怕拼写错误。

2. 反射方案(仅纯Dart项目可用)

如果是普通Dart项目(非Flutter),可以用dart:mirrors反射来根据字符串设置属性,不用手写109个case:

import 'dart:mirrors';

User setValueToUser(String propertyName, dynamic value) {
  // 先初始化一个默认User(根据实际情况调整默认值)
  final user = User(id: 0, email: '', password: '', birthday: DateTime.now());
  final userMirror = reflect(user);
  // 通过Symbol匹配属性名
  userMirror.setField(Symbol(propertyName), value);
  return user;
}

注意:Flutter项目禁用dart:mirrors,因为会影响Tree Shaking导致包体积过大,所以这个方案只适用于纯Dart场景。

3. Map转对象方案

先把User转成JSON Map,修改指定属性后再转回User对象:

User setValueToUser(User originalUser, String propertyName, dynamic value) {
  final userMap = originalUser.toJson();
  // 这里要注意类型匹配,比如DateTime需要确保value是正确格式或对象
  userMap[propertyName] = value;
  return User.fromJson(userMap);
}

这个方案简单直接,但要处理好类型转换问题,比如DateTime类型不能直接传字符串,需要先转成DateTime对象再赋值。

4. 自动代码生成

如果一定要保留setValueToUser的字符串调用形式,可以用build_runner自定义代码生成器,自动遍历User的所有属性生成switch-case代码。这样只需要运行构建命令,就能自动生成109个属性的处理逻辑,不用手动写。

你可以参考json_serializable的实现方式,自定义一个注解和生成器,专门生成属性设置的代码。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 06:51:17