start method
Start a tour: typo validation → machine → seeding of already-mounted targets. The wait-for-target timer is armed by a machine effect.
Async deliberately: (1) the typo AssertionError goes into the Future
(loud failure in debug from expectLater) instead of being thrown in
the middle of someone's build; (2) later start will await fetching a
server-driven tour — the signature is already ready and won't need a
breaking change.
Implementation
Future<void> start(HintTour tour) async {
assert(
_machine.state.isIdle,
"hintful: start('${tour.id}') while ${_machine.state} is active"
' — one tour at a time',
);
assert(
tour.duplicateTargetIds.isEmpty,
"hintful: tour '${tour.id}' has duplicate step targetIds:"
' ${tour.duplicateTargetIds.join(', ')}',
);
final classification = classifyStepTargets(
tour,
Set<String>.of(_registry.ids),
);
if (classification.typos.isNotEmpty) {
final message = _describeTypos(tour, classification.typos);
assert(false, message); // debug: loud failure with candidates
tour = _withoutTypoSteps(tour, classification.typos); // release: skip
if (tour.steps.isEmpty) return; // nothing to show
}
_dispatch(HintStart(tour: tour));
// Already-mounted targets will not fire onChange (the registry did not
// change) — seed them synchronously, otherwise waiting(0) would spin
// forever.
for (final id in _registry.ids) {
_dispatch(TargetAppeared(targetId: id));
}
_lastKnownIds = _registry.ids;
}