Flutter Freezed - Добавить универсальное поле в замороженный класс
Как заставить замороженный объект иметь универсальный тип? Я хочу сделать это:
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:vepo/src/entity_types/option_entity.dart';
part 'vegan_item_tag.freezed.dart';
part 'vegan_item_tag.g.dart';
@freezed
abstract class VeganItemTag<T>
with _$VeganItemTag<T>
implements OptionEntity<T> {
const factory VeganItemTag({int? iconCodePoint, T? id, String? name}) =
_VeganItemTag;
const VeganItemTag._();
factory VeganItemTag.fromJson(Map<String, dynamic> json) =>
_$VeganItemTagFromJson(json);
}
Я пробовал использовать
@With.fromString('AdministrativeArea<House>')
из документации, но не может правильно применить его к этому классу.
Некоторые ошибки консоли находятся в комментариях, и я также вижу эту ошибку, когда делаю это:
<GroceryItemTagEnum, VeganItemTag<GroceryItemTagEnum>>{
GroceryItemTagEnum.babyAndChild: VeganItemTag<GroceryItemTagEnum>(
name: 'Baby & Child',
id: GroceryItemTagEnum.babyAndChild,
iconCodePoint: 0xf77c),
Тип элемента _ $ _ VeganItemTag<\ dynamic> нельзя присвоить типу значения карты VeganItemTag<\ GroceryItemTagEnum>
2 ответа
Есть несколько вариантов решения этой проблемы. Но во всех из них вам нужно явно преобразовать свои классы в общий тип, который Firebase может обрабатывать, например или
Map<dynamic, String>
.
Три способа реализовать такое поведение:
FromJson ToJson
Это сложнее поддерживать, чем
JsonConverters
в сложных сценариях, поэтому я бы отказался от этого варианта для вашего решения.
JsonConverters
Он работает для автоматизации преобразований определенных классов или абстрактных классов через наследование, но из общих типов с разными данными для хранения это может быть не то, что вам нужно. Если вы всегда сохраняете одни и те же значения из универсального типа
T
вы можете попробовать использовать это решение через реализованные абстрактные классы.
GenericArgumentFactories
Это то, о чем вы на самом деле спрашиваете. Работать с genericArgumentFactories на json_serializable и непросто, и тем временем я обнаружил ошибку в Freezedпакете Freezed .
Но мне удалось заставить этот код работать, что является фактическим решением 🧭.
@freezed
@JsonSerializable(genericArgumentFactories: true)
class VeganItemTagV2<T> with _$VeganItemTagV2<T> {
const VeganItemTagV2._();
const factory VeganItemTagV2({
required int iconCodePoint,
required T id,
required String name,
}) = _VeganItemTag<T>;
//It only works with block bodies and not with expression bodies
//I don't know why
factory VeganItemTagV2.fromJson(
Map<String, dynamic> json, T Function(Object? json) fromJsonT) {
return _$VeganItemTagV2FromJson<T>(json, fromJsonT);
}
Map<String, dynamic> toJson(Object Function(T value) toJsonT) {
return _$VeganItemTagV2ToJson<T>(this, toJsonT);
}
}
Это добавляет конвертеры на
toJson
а также
fromJson
методы, которые будут использоваться в зависимости от универсального типа.
ПРИМЕЧАНИЕ. Эти методы не могут быть выражениями для какой-либо ошибки, поскольку она не компилируется, но работает с телами блоков. Freezed официально не поддерживает его, поэтому вы можете рассмотреть возможность создания этого класса без пакета Freezed.
Это пример инкапсулированного класса
String
и тестовый класс, чтобы увидеть, как это работает:
class VeganId {
final String id;
VeganId(this.id);
String itemId() {
return id;
}
@override
String toString() {
return 'VeganId{id: $id}';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is VeganId && runtimeType == other.runtimeType && id == other.id;
@override
int get hashCode => id.hashCode;
}
И тест, который отлично работает
test('veganItemV2 from and toJson', () {
final dto = VeganItemTagV2<VeganId>(
iconCodePoint: 1,
id: VeganId("veganID"),
name: "name",
);
final Map<String, dynamic> actualToJson = dto.toJson((id) => id.itemId());
expect(actualToJson, {"iconCodePoint": 1, "id": "veganID", "name": "name"});
final VeganItemTagV2 actualFromJson = VeganItemTagV2<VeganId>.fromJson(
actualToJson,
(json) =>
VeganId(json as String),
);
expect(actualFromJson, dto);
});
Ваш последний код не генерирует
vegan_item_tag.g.dart
потому что вы написали неправильный код в
VeganItemTag.fromJson
фабрика. Отредактируйте его примерно так:
factory VeganItemTag.fromJson(
Map<String, Object?> json,
T Function(Object?) fromJsonT,
) => _$VeganItemTagFromJson(json, fromJsonT);
Или же:
factory VeganItemTag.fromJson(Map<String, Object?> json) =>
_$VeganItemTagFromJson(json);
А затем повторно запустите
flutter pub run build_runner build --delete-conflicting-outputs
команда.