如何在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
相关产品推荐
相关产品推荐

