Skip to content

Support nested schema references inside Lists (e.g., List<NestedModel>) #43

@tilucasoli

Description

@tilucasoli

Feature Request

Add support for nested schema references inside Ack.list() in the code generator.

Context

Currently, when using a List with a nested custom type like List<Address>, the generator cannot reference the nested schema. This was already acknowledged as a future feature in example/lib/schema_types_edge_cases.dart (lines 126-133):

/// EXPECTED BEHAVIOR (future):
/// - addresses: List<AddressType> or List<Map<String, dynamic>>
@AckType()
final contactListSchema = Ack.object({
  'name': Ack.string(),
  // Future: List of nested schema references
  // 'addresses': Ack.list(addressSchema),
});

Use Case

For genUI integration (and similar use cases), we need to define schemas with lists of complex nested objects:

@AckType()
final addressSchema = Ack.object({
  'street': Ack.string(),
  'city': Ack.string(),
  'zipCode': Ack.string(),
  'country': Ack.string(),
});

/// Schema that references another schema variable
///
/// EXPECTED BEHAVIOR:
/// - address field should NOT be null/missing
/// - Should generate: AddressType get address (or Map<String, dynamic>)
@AckType()
final personSchema = Ack.object({
  'name': Ack.string(),
  'email': Ack.string(),
  'address': addressSchema, // Reference to another schema
  'age': Ack.integer(),
});

Expected Behavior

The generator should produce:

extension type PersonType(Map<String, Object?> _data)
    implements Map<String, Object?> {
  static PersonType parse(Object? data) {
    final validated = personSchema.parse(data);
    return PersonType(validated as Map<String, Object?>);
  }

  static SchemaResult<PersonType> safeParse(Object? data) {
    final result = personSchema.safeParse(data);
    return result.match(
      onOk: (validated) =>
          SchemaResult.ok(PersonType(validated as Map<String, Object?>)),
      onFail: (error) => SchemaResult.fail(error),
    );
  }

  String get name => _data['name'] as String;

  String get email => _data['email'] as String;

  AddressType get address => AddressType.parse(_data['address']);

  int get age => _data['age'] as int;

  PersonType copyWith({
    String? name,
    String? email,
    Map<String, dynamic>? address,
    int? age,
  }) {
    return PersonType.parse({
      'name': name ?? this.name,
      'email': email ?? this.email,
      'address': address ?? this.address,
      'age': age ?? this.age,
    });
  }
}

Technical Details

In packages/ack_generator/lib/src/builders/field_builder.dart, the _buildListSchema method (lines 95-107) currently only handles primitive types properly. It needs to be extended to detect when the list item type is a custom model with its own schema and reference that schema variable.

Impact

This is a blocker for complex AI/LLM integrations that require nested data structures in their schemas.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions