import 'package:flutter/foundation.dart'; /// PATCH 请求的**三态字段**:契约 v1.4.0 对「昵称 / 头像」两类天生可选的 /// 字段定型为三态语义(`UpdateMeRequest` 与 `UpdatePetRequest.avatarAssetId`): /// /// | 态 | JSON 表现 | 服务端语义 | /// | --- | --- | --- | /// | [PatchField.absent] | **键不出现** | 不改 | /// | [PatchField.clear] | 键出现且值为 `null` | 清空 | /// | [PatchField.value] | 键出现且有值 | 设置 | /// /// Dart 的 `String?` 只有两态(有值 / null),无法区分「不改」与「清空」—— /// 若把「不改」也编码为 `null`,未改动的字段会被服务端当成清空指令执行 /// (用户只改昵称,头像就被顺手删了)。故三态必须由类型承载, /// 不能靠 `T?` 加约定。 /// /// 序列化一律经 [writeTo]:它是「absent 不落键」这条纪律的唯一实现处, /// 各请求 DTO 不自行拼 map,避免某处漏写 `isPresent` 判断。 @immutable class PatchField { /// 不改:键不出现在 JSON 里。 const PatchField.absent() : _present = false, _value = null; /// 清空:键出现且值为 `null`。 const PatchField.clear() : _present = true, _value = null; /// 设置为 [value]。 const PatchField.value(T value) : _present = true, _value = value; final bool _present; final T? _value; /// 本次 PATCH 是否触及该字段(决定键是否落进 JSON)。 bool get isPresent => _present; /// 是否为「显式清空」(present 且值为 null)。 bool get isClear => _present && _value == null; /// present 且有值时的值;absent 与 clear 均为 null(两者不可由此区分)。 T? get valueOrNull => _value; /// 按三态把自己写进 [json]:absent 不落键;clear 落 `null`; /// 有值时落 [encode] 的产物(缺省原样写入)。 void writeTo( Map json, String key, { Object? Function(T value)? encode, }) { if (!_present) return; final value = _value; json[key] = value == null ? null : (encode?.call(value) ?? value); } @override bool operator ==(Object other) => other is PatchField && other._present == _present && other._value == _value; @override int get hashCode => Object.hash(_present, _value); @override String toString() => _present ? 'PatchField($_value)' : 'PatchField.absent()'; }