track method
Implementation
List<TrackedCollectionItem> track(
List<dynamic> values, {
required bool Function(dynamic a, dynamic b) same,
}) {
Set<int> used = <int>{};
List<TrackedCollectionItem?> next = List<TrackedCollectionItem?>.filled(
values.length,
null,
growable: false,
);
List<int> unmatched = <int>[];
// First pass: keep ids for unchanged values, preferring the nearest
// previous index to reduce churn with duplicates.
for (int newIndex = 0; newIndex < values.length; newIndex++) {
dynamic value = values[newIndex];
int matched = -1;
int bestDistance = 1 << 30;
for (int i = 0; i < _last.length; i++) {
if (used.contains(i)) {
continue;
}
if (!same(_last[i].value, value)) {
continue;
}
int distance = (i - newIndex).abs();
if (distance < bestDistance) {
bestDistance = distance;
matched = i;
if (distance == 0) {
break;
}
}
}
if (matched >= 0) {
used.add(matched);
next[newIndex] = TrackedCollectionItem(
id: _last[matched].id,
value: value,
);
} else {
unmatched.add(newIndex);
}
}
// Second pass: if a value changed in place (like typing in a text field),
// retain the id at that same index so focus/state are preserved.
for (int newIndex in unmatched) {
if (newIndex < _last.length && !used.contains(newIndex)) {
used.add(newIndex);
next[newIndex] = TrackedCollectionItem(
id: _last[newIndex].id,
value: values[newIndex],
);
}
}
// Final pass: assign new ids for true inserts.
for (int i = 0; i < next.length; i++) {
next[i] ??= TrackedCollectionItem(id: _nextId++, value: values[i]);
}
List<TrackedCollectionItem> finalized = next.cast<TrackedCollectionItem>();
_last = finalized;
return finalized;
}