matchGlassNavGroups function

  1. @visibleForTesting
List<({GlassNavBarGroup? from, GlassNavBarGroup? to})> matchGlassNavGroups(
  1. List<GlassNavBarGroup> from,
  2. List<GlassNavBarGroup> to, {
  3. bool anchoredAtStart = false,
})

Pairs two routes' groups so a shell that survives the transition morphs in place.

matchGlassNavActions one level up. A group pairs first with the group holding any of its items — by identity, or by id where the item has one — so a shell follows its items when a spacer adds or removes a group beside it. Groups left over pair by position, counted from the anchored edge as anchoredAtStart describes, and anything still unpaired enters or exits on its own.

Returned with the incoming groups first, in order, and the groups only the outgoing route has after them.

Public for testing; held back from the barrel's show clause.

Implementation

@visibleForTesting
List<({GlassNavBarGroup? from, GlassNavBarGroup? to})> matchGlassNavGroups(
  List<GlassNavBarGroup> from,
  List<GlassNavBarGroup> to, {
  bool anchoredAtStart = false,
}) {
  int slotOf(int index, int length) =>
      anchoredAtStart ? index : length - 1 - index;

  final matches = List<int?>.filled(to.length, null);
  final usedFrom = <int>{};

  // Shared items first, for every group, so no group's positional fallback
  // can take a partner another group's items have already claimed.
  for (var t = 0; t < to.length; t++) {
    for (var f = 0; f < from.length; f++) {
      if (usedFrom.contains(f) || !to[t].items.any(from[f].contains)) continue;
      matches[t] = f;
      usedFrom.add(f);
      break;
    }
  }
  for (var t = 0; t < to.length; t++) {
    if (matches[t] != null) continue;
    final wanted = slotOf(t, to.length);
    for (var f = 0; f < from.length; f++) {
      if (usedFrom.contains(f) || slotOf(f, from.length) != wanted) continue;
      matches[t] = f;
      usedFrom.add(f);
      break;
    }
  }

  return [
    for (var t = 0; t < to.length; t++)
      (from: matches[t] == null ? null : from[matches[t]!], to: to[t]),
    for (var f = 0; f < from.length; f++)
      if (!usedFrom.contains(f)) (from: from[f], to: null),
  ];
}