Skip to content

Commit bbba77c

Browse files
committed
Allow flex for dynamic/Object? without flex annotation #114
1 parent f7e6147 commit bbba77c

File tree

5 files changed

+94
-33
lines changed

5 files changed

+94
-33
lines changed

generator/lib/src/entity_resolver.dart

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,7 @@ class EntityResolver extends Builder {
203203

204204
// Error if Flex is used on unsupported type
205205
if (fieldType == OBXPropertyType.Flex) {
206-
final isMap = f.type.isDartCoreMap;
207-
final isList = f.type.isDartCoreList;
208-
// dynamic is always nullable; Object requires nullable annotation
209-
final isValue =
210-
f.type is DynamicType ||
211-
(f.type.isDartCoreObject &&
212-
f.type.nullabilitySuffix == NullabilitySuffix.question);
213-
if (!isMap && !isList && !isValue) {
206+
if (!_isSupportedFlexType(f.type)) {
214207
throw InvalidGenerationSourceError(
215208
"'${classElement.displayName}.${f.displayName}': PropertyType.flex can only be used with "
216209
"Map, List, dynamic, and Object?, but is '${f.type}'.",
@@ -461,14 +454,8 @@ class EntityResolver extends Builder {
461454
} else if (itemType.isDartCoreString) {
462455
// List<String>
463456
return OBXPropertyType.StringVector;
464-
} else if (_isDynamicOrObject(itemType)) {
465-
// List<dynamic>, List<Object?>, or List<Object>
457+
} else if (_isSupportedFlexType(dartType)) {
466458
return OBXPropertyType.Flex;
467-
} else if (itemType.isDartCoreMap) {
468-
// List<Map<String, dynamic/Object?>> - Object not supported (cast issue)
469-
if (_isMapSupportedForFlex(itemType)) {
470-
return OBXPropertyType.Flex;
471-
}
472459
}
473460
} else if ([
474461
'Int8List',
@@ -502,11 +489,9 @@ class EntityResolver extends Builder {
502489
return OBXPropertyType.Date;
503490
} else if (isToOneRelationField(field)) {
504491
return OBXPropertyType.Relation;
505-
} else if (dartType.isDartCoreMap) {
506-
// Check for Map<String, dynamic/Object?/Object>
507-
if (_isMapSupportedForFlex(dartType)) {
508-
return OBXPropertyType.Flex;
509-
}
492+
} else if (_isSupportedFlexType(dartType)) {
493+
// dynamic, Object?, Map<String, dynamic/Object?/Object>, or List<...>
494+
return OBXPropertyType.Flex;
510495
}
511496

512497
// No supported Dart type recognized.
@@ -517,6 +502,34 @@ class EntityResolver extends Builder {
517502
return dartType is DynamicType || dartType.isDartCoreObject;
518503
}
519504

505+
/// Returns true if [type] is supported for Flex properties:
506+
/// - dynamic
507+
/// - Object? (nullable Object)
508+
/// - `Map<String, dynamic/Object?/Object>`
509+
/// - `List<dynamic/Object?/Object>` or `List<Map<String, dynamic/Object?>>`
510+
bool _isSupportedFlexType(DartType type) {
511+
// dynamic is always nullable
512+
if (type is DynamicType) return true;
513+
// Object? (nullable Object)
514+
if (type.isDartCoreObject &&
515+
type.nullabilitySuffix == NullabilitySuffix.question) {
516+
return true;
517+
}
518+
// Map<String, dynamic/Object?/Object>
519+
if (type.isDartCoreMap && _isMapSupportedForFlex(type)) return true;
520+
// List<dynamic/Object?/Object> or List<Map<String, dynamic/Object?>>
521+
if (type.isDartCoreList) {
522+
final itemType = listItemType(type);
523+
if (itemType != null) {
524+
if (_isDynamicOrObject(itemType)) return true;
525+
if (itemType.isDartCoreMap && _isMapSupportedForFlex(itemType)) {
526+
return true;
527+
}
528+
}
529+
}
530+
return false;
531+
}
532+
520533
bool _isMapSupportedForFlex(DartType dartType) {
521534
if (dartType is ParameterizedType && dartType.typeArguments.length == 2) {
522535
final keyType = dartType.typeArguments[0];

generator/test/code_builder_test.dart

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -896,21 +896,26 @@ void main() {
896896
@Id()
897897
int id = 0;
898898
899-
// Explicit annotation required for dynamic
900-
@Property(type: PropertyType.flex)
899+
// Auto-detected dynamic
901900
dynamic flexDynamic;
902901
903-
// Explicit annotation required for Object?
902+
// Auto-detected Object?
903+
Object? flexObject;
904+
905+
// Explicit annotation still works
906+
@Property(type: PropertyType.flex)
907+
dynamic flexDynamicExplicit;
908+
904909
@Property(type: PropertyType.flex)
905-
Object? flexObject;
910+
Object? flexObjectExplicit;
906911
}
907912
''';
908913

909914
final testEnv = GeneratorTestEnv();
910915
await testEnv.run(source);
911916

912917
var properties = testEnv.model.entities[0].properties;
913-
expectFlexProperties(properties, 3);
918+
expectFlexProperties(properties, 5);
914919
});
915920

916921
test('Flex unsupported type errors', () async {

objectbox_test/test/entity_flex.dart

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,24 @@ class FlexValueEntity {
7676
@Id()
7777
int id = 0;
7878

79-
// Explicit annotation required for dynamic
80-
@Property(type: PropertyType.flex)
79+
// Auto-detected dynamic
8180
dynamic flexDynamic;
8281

83-
// Explicit annotation required for Object?
84-
@Property(type: PropertyType.flex)
82+
// Auto-detected "Object?"
8583
Object? flexObject;
8684

85+
// Explicit annotation for dynamic
86+
@Property(type: PropertyType.flex)
87+
dynamic flexDynamicExplicit;
88+
89+
// Explicit annotation for "Object?"
90+
@Property(type: PropertyType.flex)
91+
Object? flexObjectExplicit;
92+
8793
FlexValueEntity({
8894
this.flexDynamic,
8995
this.flexObject,
96+
this.flexDynamicExplicit,
97+
this.flexObjectExplicit,
9098
});
9199
}

objectbox_test/test/flex_test.dart

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,18 +407,31 @@ void main() {
407407
final id = box.put(entity);
408408

409409
final read = box.get(id)!;
410+
// Auto-detected
410411
expect(read.flexDynamic, isNull);
411412
expect(read.flexObject, isNull);
413+
// Explicitly annotated
414+
expect(read.flexDynamicExplicit, isNull);
415+
expect(read.flexObjectExplicit, isNull);
412416
});
413417

414418
test('put and get value with various types', () {
415419
assertPutAndGet<V>(V value) {
416-
final entity = FlexValueEntity(flexDynamic: value, flexObject: value);
420+
final entity = FlexValueEntity(
421+
flexDynamic: value,
422+
flexObject: value,
423+
flexDynamicExplicit: value,
424+
flexObjectExplicit: value,
425+
);
417426
final id = box.put(entity);
418427

419428
final read = box.get(id)!;
429+
// Auto-detected
420430
expect(read.flexDynamic, value);
421431
expect(read.flexObject, value);
432+
// Explicitly annotated
433+
expect(read.flexDynamicExplicit, value);
434+
expect(read.flexObjectExplicit, value);
422435
}
423436

424437
assertPutAndGet('hello world');
@@ -431,36 +444,48 @@ void main() {
431444

432445
test('update value type', () {
433446
// Start with a string
434-
final entity = FlexValueEntity(flexDynamic: 'initial');
447+
final entity = FlexValueEntity(
448+
flexDynamic: 'initial',
449+
flexDynamicExplicit: 'initial',
450+
);
435451
final id = box.put(entity);
436452

437453
// Update to an int
438454
final read = box.get(id)!;
439455
read.flexDynamic = 123;
456+
read.flexDynamicExplicit = 123;
440457
box.put(read);
441458

442459
// Verify update
443460
final updated = box.get(id)!;
444461
expect(updated.flexDynamic, 123);
462+
expect(updated.flexDynamicExplicit, 123);
445463

446464
// Update to a map
447465
updated.flexDynamic = {'changed': true};
466+
updated.flexDynamicExplicit = {'changed': true};
448467
box.put(updated);
449468

450469
final final_ = box.get(id)!;
451470
expect((final_.flexDynamic as Map)['changed'], true);
471+
expect((final_.flexDynamicExplicit as Map)['changed'], true);
452472
});
453473

454474
test('set value to null', () {
455-
final entity = FlexValueEntity(flexDynamic: 'not null');
475+
final entity = FlexValueEntity(
476+
flexDynamic: 'not null',
477+
flexDynamicExplicit: 'not null',
478+
);
456479
final id = box.put(entity);
457480

458481
final read = box.get(id)!;
459482
read.flexDynamic = null;
483+
read.flexDynamicExplicit = null;
460484
box.put(read);
461485

462486
final updated = box.get(id)!;
463487
expect(updated.flexDynamic, isNull);
488+
expect(updated.flexDynamicExplicit, isNull);
464489
});
465490
});
466491
}

objectbox_test/test/objectbox-model.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@
916916
},
917917
{
918918
"id": "22:3648782640383158768",
919-
"lastPropertyId": "3:7299173100433666540",
919+
"lastPropertyId": "5:4857837946721321832",
920920
"name": "FlexValueEntity",
921921
"properties": [
922922
{
@@ -934,6 +934,16 @@
934934
"id": "3:7299173100433666540",
935935
"name": "flexObject",
936936
"type": 13
937+
},
938+
{
939+
"id": "4:6926670937343705217",
940+
"name": "flexDynamicExplicit",
941+
"type": 13
942+
},
943+
{
944+
"id": "5:4857837946721321832",
945+
"name": "flexObjectExplicit",
946+
"type": 13
937947
}
938948
],
939949
"relations": []

0 commit comments

Comments
 (0)