Flutter may dispose widgets outside a lazy viewport. Form values, errors, and row identity live in the controller, so a row's state does not disappear just because its widget unmounts.
KeyedFieldList<Root, Item> observes the list's row set and order; edits within each row are handled by the nested field bindings.
Build each row with its stable clientId, not its current index, both for the widget key and generated nested field refs:
KeyedFieldList<TourSchema, StopSchema>(
field: TourFields.stops,
builder: (context, stops, list) => ListView.builder(
itemCount: stops.length,
itemBuilder: (context, index) {
final stop = stops[index];
final fields = TourFields.stop(stopClientId: stop.clientId);
return Card(
key: ValueKey(stop.clientId),
child: KeyedFormField.text<TourSchema>(
field: fields.city,
builder: (context, field, text) => TextField(
controller: text,
decoration: InputDecoration(errorText: field.errorText),
),
),
);
},
),
)
A KeyedFieldList rebuilds when rows are added, removed, or reordered; it does not rebuild just because a field inside a row changed. The provided
KeyedFormList supports collection operations while generated field references address nested data by row identity. See the
packing-list walkthrough and the packing example source.
A mounted field can be revealed precisely on submit. An unbuilt lazy row has no anchor yet, so long-list error navigation needs the application's coarse section scroll before registry reveal; see submit and scroll to error.