keyed_form_core 0.1.0 copy "keyed_form_core: ^0.1.0" to clipboard
keyed_form_core: ^0.1.0 copied to clipboard

Shared vocabulary for the keyed_form family: FieldRef / StrictFieldRef / VariantRef field references over keyed_lens optics, DelegatingFieldRef, FieldErrors, and the @keyedSchema annotation.

example/keyed_form_core_example.dart

// The shared "field reference" vocabulary the whole keyed_form family speaks.
//
// `keyed_form_core` sits on `keyed_lens` but never says "lens": a field is a
// `FieldRef` / `StrictFieldRef` / `VariantRef`, and per-field data (errors,
// dirty flags, …) lives in a `FieldErrors` sidecar looked up *by that same
// ref*. This is exactly the shape `keyed_form_gen` emits — written by hand
// here so you can see it.
//
//   dart run example/keyed_form_core_example.dart

import 'package:keyed_form_core/keyed_form_core.dart';

// ── Model ─────────────────────────────────────────────────────────────────

class Tour {
  const Tour({this.title = '', this.stops = const [], this.notes});

  final String title;
  final List<Stop> stops;
  final String? notes;

  Tour copyWith({String? title, List<Stop>? stops, Object? notes = _unset}) =>
      Tour(
        title: title ?? this.title,
        stops: stops ?? this.stops,
        notes: identical(notes, _unset) ? this.notes : notes as String?,
      );
}

class Stop implements KeyedRow {
  const Stop({required this.clientId, this.city = '', this.nights = 1});

  @override
  final String clientId;
  final String city;
  final int nights;

  Stop copyWith({String? city, int? nights}) => Stop(
    clientId: clientId,
    city: city ?? this.city,
    nights: nights ?? this.nights,
  );
}

const _unset = Object();

// ── Field references ──────────────────────────────────────────────────────
// `StrictFieldRef` for scalars that always resolve; a `DelegatingFieldRef`
// wrapper for a list row, whose leaf getters are affine (`FieldRef`) because
// the row may be gone.

abstract final class StopFields {
  static StrictFieldRef<Stop, String> get city =>
      StrictFieldRef<Stop, String>.of(
        key: FieldKey.name('city'),
        get: (s) => s.city,
        set: (s, v) => s.copyWith(city: v),
      );

  static StrictFieldRef<Stop, int> get nights => StrictFieldRef<Stop, int>.of(
    key: FieldKey.name('nights'),
    get: (s) => s.nights,
    set: (s, v) => s.copyWith(nights: v),
  );
}

final class StopFieldRefs extends DelegatingFieldRef<Tour, Stop> {
  StopFieldRefs(super.inner);

  FieldRef<Tour, String> get city => inner.then(StopFields.city);
  FieldRef<Tour, int> get nights => inner.then(StopFields.nights);
}

abstract final class TourFields {
  static StrictFieldRef<Tour, String> get title =>
      StrictFieldRef<Tour, String>.of(
        key: FieldKey.name('title'),
        get: (t) => t.title,
        set: (t, v) => t.copyWith(title: v),
      );

  static StrictFieldRef<Tour, List<Stop>> get stops =>
      StrictFieldRef<Tour, List<Stop>>.of(
        key: FieldKey.name('stops'),
        get: (t) => t.stops,
        set: (t, v) => t.copyWith(stops: v),
      );

  static StrictFieldRef<Tour, String?> get notes =>
      StrictFieldRef<Tour, String?>.of(
        key: FieldKey.name('notes'),
        get: (t) => t.notes,
        set: (t, v) => t.copyWith(notes: v),
      );

  /// Affine field references for one `stops` row, addressed by clientId.
  static StopFieldRefs stop(String clientId) =>
      StopFieldRefs(stops.at(clientId, (s) => s.clientId == clientId));
}

void main() {
  var tour = const Tour(
    title: 'Ky',
    stops: [
      Stop(clientId: 'a', city: 'Kyoto', nights: 3),
      Stop(clientId: 'b', city: '', nights: 1),
    ],
  );

  // 1. Read / write through the vocabulary — same handle either direction.
  tour = TourFields.title.set(tour, 'Kyoto in autumn');
  tour = TourFields.stop('b').city.set(tour, 'Nara');
  print(TourFields.stop('b').city.getOrNull(tour)); // Nara

  // 2. A ref to a row that is gone reads null and writes no-op.
  print(TourFields.stop('gone').nights.getOrNull(tour)); // null

  // 3. FieldErrors — a FieldKey-keyed sidecar, but you look it up by *ref*.
  final errors = FieldErrors<String>({
    TourFields.title.key: 'Too short',
    TourFields.stop('a').city.key: 'Unknown city',
  });
  print(errors(TourFields.stop('a').city)); // Unknown city
  print(errors(TourFields.stop('b').city)); // null

  // 4. whenPresent() refines a nullable field to its non-null value.
  final notes = TourFields.notes.whenPresent();
  print(notes.getOrNull(tour)); // null — the field holds null
  tour = TourFields.notes.set(tour, 'Bring a warm coat');
  print(notes.getOrNull(tour)); // Bring a warm coat

  // 5. removeSubtree() drops a row's errors when the row is removed, so a
  //    stale message never outlives the thing it described.
  final pruned = errors.removeSubtree(TourFields.stop('a').key);
  print('${errors.length} -> ${pruned.length}'); // 2 -> 1
}
1
likes
150
points
--
downloads

Documentation

API reference

Publisher

verified publisherv4g.space

Weekly Downloads

Shared vocabulary for the keyed_form family: FieldRef / StrictFieldRef / VariantRef field references over keyed_lens optics, DelegatingFieldRef, FieldErrors, and the @keyedSchema annotation.

Homepage
Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

keyed_lens

More

Packages that depend on keyed_form_core