splitKey static method

({String? description, String? key}) splitKey(
  1. String? description,
  2. String type
)

Form-[LabeledGlobalKey<FormState>#acc1d] → ('Form', '[LabeledGlobalKey<FormState>#]').

The framework's Widget.toStringShort() is '$type-$key', so a keyed widget reaches either converter with two facts fused into one string. This takes them apart, and is the only place that does — the guest and the VM-service path both call it, on the same string, so a tree read through one is spelled exactly like a tree read through the other.

The identity hash goes. A key with no value of its own stringifies through describeIdentity/shortHash, which is hashCode — assigned per allocation, so the same GlobalKey<FormState>() spells itself #acc1d in one process and #0507e in the next. InspectNode exists to be carried between processes; a value that means nothing outside the one that minted it has no business travelling in it. Left in, it cost a previews comparison 21 entries reported as changed with zero pixels changed, each one a Form failing to match itself — and worse than the noise, an unmatched node's subtree is never walked, so every one of those false positives was hiding whatever really did change beneath it.

Returns the description with the key removed, which for the common case is the bare type and so goes on to be dropped as saying nothing.

Implementation

static ({String? description, String? key}) splitKey(
  String? description,
  String type,
) {
  const none = (description: null, key: null);
  if (description == null) return none;
  var open = type.length + 1;
  // `type` + `-` + at least `[]`.
  if (description.length < open + 2 ||
      !description.startsWith(type) ||
      description[type.length] != '-' ||
      description[open] != '[') {
    return (description: description, key: null);
  }
  // Balanced rather than "up to the last `]`": a `ValueKey` of a list spells
  // itself `[<[1, 2]>]`, and a description can carry properties after the
  // key that have brackets of their own.
  var depth = 0;
  for (var i = open; i < description.length; i++) {
    switch (description[i]) {
      case '[':
        depth++;
      case ']':
        depth--;
        if (depth == 0) {
          return (
            description: type + description.substring(i + 1),
            key: withoutIdentityHash(description.substring(open, i + 1)),
          );
        }
    }
  }
  return (description: description, key: null);
}