Implementation
@override
String generate(List<ClassMember> members) {
var toMap = '';
var patcher = '';
var usingToDouble = false;
var usingToDecimal = false;
final toDouble =
'final toDouble = (val) => val == null ? null : val * 1.0;\n';
final toDecimal =
'final toDecimal = (val) => val == null ? null : Decimal.parse(val.toString());\n';
for (var member in members) {
if (!(member is FieldDeclaration)) continue;
if (!getTag(member).contains('json')) continue;
final key = getTag(member).split(':')[1].replaceAll('"', '');
final isNullable = member.fields.type?.question != null;
final dot = isNullable ? '?.' : '.';
final typeName = (member.fields.type as NamedType).name.toString();
final typeArgs =
(member.fields.type as NamedType).typeArguments?.arguments;
final leftType = (typeArgs?.elementAtOrNull(0) as NamedType?);
final leftName = leftType?.name.toString() ?? 'dynamic';
final leftDot = (leftType?.question?.toString() ?? '') + '.';
final leftExcl = leftType?.question == null ? '!' : '';
final rightType = (typeArgs?.elementAtOrNull(1) as NamedType?);
final rightName = rightType?.name.toString() ?? 'dynamic';
final rightDot = (rightType?.question?.toString() ?? '') + '.';
final rightExcl = rightType?.question == null ? '!' : '';
final type = member.fields.type
.toString()
.replaceAll('\$', '')
.replaceAll('?', '');
final name = member.fields.variables.first.name.lexeme;
final initializer = member.fields.variables.first.initializer;
if ([
'String',
'num',
'bool',
'int',
'dynamic',
'Map<String, dynamic>',
'List<dynamic>'
].contains(type)) {
toMap += "'$key': $name,\n";
patcher += "$name = _data['$key']";
} else if (type == 'double') {
toMap += "'$key': $name,\n";
patcher += "$name = toDouble(_data['$key'])";
usingToDouble = true;
} else if (type == 'Decimal') {
toMap += "'$key': $name${dot}toDouble(),\n";
patcher += "$name = toDecimal(_data['$key'])";
usingToDecimal = true;
} else if (enums.contains(type)) {
toMap += "'$key': $name${dot}value,\n";
patcher += "$name = $type.parse(_data['$key'])";
} else if (typeName == 'Map') {
// Determine if key needs string conversion for JSON serialization
final isKeyString = leftName == 'String';
final needsKeyConversion =
!isKeyString && ['num', 'bool', 'int', 'double'].contains(leftName);
// Helper to convert key to string in toMap
final keyToString = needsKeyConversion ? 'k.toString()' : 'k';
// Helper to parse key from string in patcher
String parseKey(String keyType) {
switch (keyType) {
case 'int':
return 'int.parse(k)';
case 'double':
return 'double.parse(k)';
case 'num':
return 'num.parse(k)';
case 'bool':
return "k == 'true'";
default:
return 'k as $keyType';
}
}
final parsedKey =
needsKeyConversion ? parseKey(leftName) : 'k as $leftName';
if (['String', 'num', 'bool', 'dynamic'].contains(rightName)) {
// For primitive values
if (needsKeyConversion || leftName == 'dynamic') {
toMap +=
"'$key': $name${dot}map<String, dynamic>((k,v) => MapEntry($keyToString, v)),\n";
} else {
toMap += "'$key': $name,\n";
}
patcher +=
"$name = (_data['$key'] as Map?)?.map<$leftName, $rightName>((k, v) => MapEntry($parsedKey, v as $rightName))";
} else if (rightName == 'int') {
if (needsKeyConversion || leftName == 'dynamic') {
toMap +=
"'$key': $name${dot}map<String, dynamic>((k,v) => MapEntry($keyToString, v)),\n";
} else {
toMap += "'$key': $name,\n";
}
patcher +=
"$name = (_data['$key'] as Map?)?.map<$leftName, $rightName>((k, v) => MapEntry($parsedKey, v ~/ 1))";
} else if (rightName == 'double') {
if (needsKeyConversion || leftName == 'dynamic') {
toMap +=
"'$key': $name${dot}map<String, dynamic>((k,v) => MapEntry($keyToString, v)),\n";
} else {
toMap += "'$key': $name,\n";
}
patcher +=
"$name = (_data['$key'] as Map?)?.map<$leftName, $rightName>((k, v) => MapEntry($parsedKey, v * 1.0))";
} else if (enums.contains(rightName)) {
toMap +=
"'$key': $name${dot}map<String, dynamic>((k,v) => MapEntry($keyToString, v${rightDot}value)),\n";
patcher +=
"$name = (_data['$key'] as Map?)?.map<$leftName, $rightName>((k, v) => MapEntry($parsedKey, $rightName.parse(v)$rightExcl))";
} else if (rightName == 'List') {
// Handle Map<K, List<V>> types
final listValueType = (rightType?.typeArguments?.arguments
.elementAtOrNull(0) as NamedType?);
final listValueName = listValueType?.name.toString() ?? 'dynamic';
final listValueDot =
(listValueType?.question?.toString() ?? '') + '.';
final listValueExcl = listValueType?.question == null ? '!' : '';
// Get the full type string for the List (e.g., "List<Address>")
final fullListType =
rightType.toString().replaceAll('\$', '').replaceAll('?', '');
if (['String', 'num', 'bool', 'dynamic', 'int', 'double']
.contains(listValueName)) {
if (needsKeyConversion || leftName == 'dynamic') {
toMap +=
"'$key': $name${dot}map<String, dynamic>((k,v) => MapEntry($keyToString, v)),\n";
} else {
toMap += "'$key': $name,\n";
}
patcher +=
"$name = (_data['$key'] as Map?)?.map<$leftName, $fullListType>((k, v) => MapEntry($parsedKey, (v as List?)?.cast<$listValueName>() ?? []))";
} else if (enums.contains(listValueName)) {
toMap +=
"'$key': $name${dot}map<String, dynamic>((k,v) => MapEntry($keyToString, v${rightDot}map((i) => i${listValueDot}value).toList())),\n";
patcher +=
"$name = (_data['$key'] as Map?)?.map<$leftName, $fullListType>((k, v) => MapEntry($parsedKey, (v as List?)?.map((i) => $listValueName.parse(i)$listValueExcl).toList().cast<$listValueName>() ?? []))";
} else {
toMap +=
"'$key': $name${dot}map<String, dynamic>((k,v) => MapEntry($keyToString, v${rightDot}map((i) => i${listValueDot}toMap()).toList())),\n";
patcher +=
"$name = (_data['$key'] as Map?)?.map<$leftName, $fullListType>((k, v) => MapEntry($parsedKey, (v as List?)?.map((i) => $listValueName.fromMap(i)$listValueExcl).toList().cast<$listValueName>() ?? []))";
}
} else {
toMap +=
"'$key': $name${dot}map<String, dynamic>((k,v) => MapEntry($keyToString, v${rightDot}toMap())),\n";
patcher +=
"$name = (_data['$key'] as Map?)?.map<$leftName, $rightName>((k, v) => MapEntry($parsedKey, $rightName.fromMap(v)$rightExcl))";
}
} else if (typeName == 'List') {
if (['String', 'num', 'bool', 'dynamic'].contains(leftName)) {
toMap += "'$key': $name,\n";
patcher += "$name = _data['$key']?.cast<$leftName>()";
} else if (leftName == 'int') {
toMap += "'$key': $name,\n";
patcher +=
"$name = _data['$key']?.map((i) => i ~/ 1).toList().cast<int>()";
} else if (leftName == 'double') {
toMap += "'$key': $name,\n";
patcher +=
"$name = _data['$key']?.map((i) => i * 1.0).toList().cast<double>()";
} else if (enums.contains(leftName)) {
toMap +=
"'$key': $name${dot}map((i) => i${leftDot}value).toList(),\n";
patcher +=
"$name = _data['$key']?.map((i) => $leftName.parse(i)$leftExcl).toList().cast<$leftName>()";
} else {
toMap +=
"'$key': $name${dot}map((i) => i${leftDot}toMap()).toList(),\n";
patcher +=
"$name = _data['$key']?.map((i) => $leftName.fromMap(i)$leftExcl).toList().cast<$leftName>()";
}
} else {
toMap += "'$key': $name${dot}toMap(),";
patcher += "$name = $type.fromMap(_data['$key'])";
}
patcher += ' ?? ${initializer ?? name};\n';
}
return '''
void patch(Map? _data) { if(_data == null) return;
${usingToDouble ? toDouble : ''}
${usingToDecimal ? toDecimal : ''}
$patcher
}
static $className? fromMap(Map? data) { if(data == null) return null; return $className()..patch(data); }
Map<String, dynamic> toMap() => {
$toMap
};
''';
}