Open machinescream opened 4 years ago
Sadly, it does not, but it's being worked on. For now, you can manually write a class that implements TypeAdapter<T>
and has an id
field. Then you can register it for multiple IDs:
Hive
..registerAdapter(MyAdapter<int>(id: 10)
..registerAdapter(MyAdapter<double>(id: 11)
Hi @marcelgarus @leisim , I was working on a desktop app and I need to save slightly complex data which also includes Generics, so I'd really appreciate if you could guide me on how to save my data in Hive. Other than the error, it'd be really nice of you to give me a few pointers on what I might be doing wrong here.
Note: The object I want to save is CodeTemplate
.
Error
Launching lib/main.dart on macOS in debug mode...
Building macOS application...
Compiler message:
lib/utils/models.g.dart:131:34: Error: 'T' isn't a type.
defaultValue: fields[3] as T,
^
lib/utils/models.g.dart:135:30: Error: 'T' isn't a type.
..value = fields[2] as T
^
My Data Structure
@HiveType(typeId: 2)
class CodeTemplate extends HiveObject {
@HiveField(1)
String name;
@HiveField(2)
String language;
@HiveField(3)
Map<int, TemplateSequenceOption> sequence;
CodeTemplate({
@required this.name,
@required this.language,
@required this.sequence,
});
}
@HiveType(typeId: 3)
class TemplateSequenceOption extends HiveObject {
@HiveField(1)
int id;
@HiveField(2)
String name;
@HiveField(3)
Color color;
@HiveField(4)
IconData iconData;
@HiveField(5)
TemplateSequenceOptionType type;
@HiveField(6)
Map<String, TemplateSequenceProperty> properties;
TemplateSequenceOption(this.id, this.name, this.color, this.iconData,
this.type, this.properties);
}
@HiveType(typeId: 4)
class TemplateSequenceProperty<T> extends HiveObject {
@HiveField(1)
int id;
@HiveField(2)
T value;
@HiveField(3)
T defaultValue;
@HiveField(4)
String hintText;
@HiveField(5)
Type dataType;
@HiveField(6)
bool optional;
TemplateSequenceProperty(this.id,
{this.defaultValue, this.hintText, this.optional = true})
: this.dataType = T;
}
My Data Saving Code
await Hive.initFlutter(); // Called once only
Hive.registerAdapter(CodeTemplateAdapter()); // Called once only
await Hive.openBox<CodeTemplate>(codeTemplatesBox); // Called once only
CodeTemplate template = CodeTemplate(
name: _nameController.text,
language: _selectedLanguage,
sequence: _sequence,
);
Box<CodeTemplate> _codeTemplatesBox = Hive.box(codeTemplatesBox);
await _codeTemplatesBox.add(template);
Models.G.Dart
...
class TemplateSequencePropertyAdapter
extends TypeAdapter<TemplateSequenceProperty> {
@override
final typeId = 4;
@override
TemplateSequenceProperty read(BinaryReader reader) {
var numOfFields = reader.readByte();
var fields = <int, dynamic>{
for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return TemplateSequenceProperty(
fields[1] as int,
defaultValue: fields[3] as T, // ---------- Line 131
hintText: fields[4] as String,
optional: fields[6] as bool,
)
..value = fields[2] as T // ---------- Line 135
..dataType = fields[5] as Type;
}
@override
void write(BinaryWriter writer, TemplateSequenceProperty obj) {
writer
..writeByte(6)
..writeByte(1)
..write(obj.id)
..writeByte(2)
..write(obj.value)
..writeByte(3)
..write(obj.defaultValue)
..writeByte(4)
..write(obj.hintText)
..writeByte(5)
..write(obj.dataType)
..writeByte(6)
..write(obj.optional);
}
}
Hive doesn't support generics out-of-the box. Yet. That means, generating code for generic classes doesn't work yet. Which in turn means, you need to write your own custom type adapter.
Here's a modified version of the automatically generated TemplateSequencePropertyAdapter
, which supports generics. You can copy it into your project (I only changed the first 6 lines):
class TemplateSequencePropertyAdapter<T>
extends TypeAdapter<TemplateSequenceProperty<T>> {
TemplateSequencePropertyAdapter({@required this.typeId}) : assert(typeId != null);
@override
final int typeId;
@override
TemplateSequenceProperty read(BinaryReader reader) {
var numOfFields = reader.readByte();
var fields = <int, dynamic>{
for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return TemplateSequenceProperty(
fields[1] as int,
defaultValue: fields[3] as T,
hintText: fields[4] as String,
optional: fields[6] as bool,
)
..value = fields[2] as T
..dataType = fields[5] as Type;
}
@override
void write(BinaryWriter writer, TemplateSequenceProperty obj) {
writer
..writeByte(6)
..writeByte(1)
..write(obj.id)
..writeByte(2)
..write(obj.value)
..writeByte(3)
..write(obj.defaultValue)
..writeByte(4)
..write(obj.hintText)
..writeByte(5)
..write(obj.dataType)
..writeByte(6)
..write(obj.optional);
}
}
You can then register this adapter for all the generic types you want to support:
await Hive.initFlutter();
Hive
..registerAdapter(CodeTemplateAdapter())
..registerAdapter(TemplateSequenceOptionAdapter())
..registerAdapter(TemplateSequencePropertyAdapter<int>(typeId: 9))
..registerAdapter(TemplateSequencePropertyAdapter<String>(typeId: 10))
..registerAdapter(TemplateSequencePropertyAdapter<double>(typeId: 11));
await Hive.openBox<CodeTemplate>(codeTemplatesBox);
Finally, you can use this hand-written adapter in your code:
CodeTemplate template = CodeTemplate(
name: _nameController.text,
language: _selectedLanguage,
sequence: _sequence,
);
Box<CodeTemplate> _codeTemplatesBox = Hive.box(codeTemplatesBox);
await _codeTemplatesBox.add(template);
I know this is loads of boilerplate, but I suppose type-safety comes built-in with Hive 2.0.
@marcelgarus Thanks for the response, one last thing, since we've manually written the adapter, I should remove the @HiveType and @HiveField properties from CodeTemplateProperty
still extend it from a HiveObject
. Right?
And can the generic be of another HiveObject type? For example, I need to use this in my code:
TemplateSequenceProperty<CodeSnippet> property = TemplateSequenceProperty<CodeSnippet>();
//Code Snippet extends from a HiveObject and has a TypeAdapter with typeId: 1
Yes exactly, the annotations should be removed, but you should still extend HiveObject
if you want save()
functions etc.
It shouldn't be a problem that the generic type also extends HiveObject
.
@MarcelGarus Hi any idea if genericType has been implemented ever since ? Thanks in advance !
AFAIK, that's not planned for the future. Hive creator @leisim is working on the new database Isar, so Hive is somewhat in a feature-freeze.
Hive doesn't support generics out-of-the box. Yet. That means, generating code for generic classes doesn't work yet. Which in turn means, you need to write your own custom type adapter.
Here's a modified version of the automatically generated
TemplateSequencePropertyAdapter
, which supports generics. You can copy it into your project (I only changed the first 6 lines):class TemplateSequencePropertyAdapter<T> extends TypeAdapter<TemplateSequenceProperty<T>> { TemplateSequencePropertyAdapter({@required this.typeId}) : assert(typeId != null); @override final int typeId; @override TemplateSequenceProperty read(BinaryReader reader) { var numOfFields = reader.readByte(); var fields = <int, dynamic>{ for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; return TemplateSequenceProperty( fields[1] as int, defaultValue: fields[3] as T, hintText: fields[4] as String, optional: fields[6] as bool, ) ..value = fields[2] as T ..dataType = fields[5] as Type; } @override void write(BinaryWriter writer, TemplateSequenceProperty obj) { writer ..writeByte(6) ..writeByte(1) ..write(obj.id) ..writeByte(2) ..write(obj.value) ..writeByte(3) ..write(obj.defaultValue) ..writeByte(4) ..write(obj.hintText) ..writeByte(5) ..write(obj.dataType) ..writeByte(6) ..write(obj.optional); } }
You can then register this adapter for all the generic types you want to support:
await Hive.initFlutter(); Hive ..registerAdapter(CodeTemplateAdapter()) ..registerAdapter(TemplateSequenceOptionAdapter()) ..registerAdapter(TemplateSequencePropertyAdapter<int>(typeId: 9)) ..registerAdapter(TemplateSequencePropertyAdapter<String>(typeId: 10)) ..registerAdapter(TemplateSequencePropertyAdapter<double>(typeId: 11)); await Hive.openBox<CodeTemplate>(codeTemplatesBox);
Finally, you can use this hand-written adapter in your code:
CodeTemplate template = CodeTemplate( name: _nameController.text, language: _selectedLanguage, sequence: _sequence, ); Box<CodeTemplate> _codeTemplatesBox = Hive.box(codeTemplatesBox); await _codeTemplatesBox.add(template);
I know this is loads of boilerplate, but I suppose type-safety comes built-in with Hive 2.0.
Still works. Thanks!