runConfigurator function
Runs the 12-section Customize configurator (FR-004, FR-039).
Read-only/in-memory throughout (FR-040): every section only mutates
_ConfiguratorState in process memory and ends by producing a Blueprint.
Implementation
Future<ConfiguratorRunResult> runConfigurator(
WizardIo io, {
required Catalog catalog,
required RecipeLock lock,
String? initialProjectName,
bool Function() isRoutingSkipValid = _routingSkipNotYetProven,
// Feeds live compatibility feedback (FR-041, T035); defaults to the
// lock's own proven versions when the caller has not yet observed the
// real toolchain (interactive feedback only — create.dart's authoritative
// check after the Blueprint is fully assembled always uses the real
// observed versions).
String? flutterVersion,
String? dartVersion,
}) async {
var state = _ConfiguratorState(projectName: initialProjectName ?? 'my_app');
var index = 0;
while (true) {
final section = configuratorSections[index];
if (section.kind == SectionKind.summary) {
_printCompatibilityFeedback(
io,
state,
catalog,
lock,
flutterVersion,
dartVersion,
);
displayBlueprintSummary(
io,
projectName: state.projectName,
organization: state.organization,
destination: state.destination,
targetPlatforms: state.targetPlatforms,
gitPreference: state.gitPreference,
singleSelections: state.singleSelections,
multiSelections: state.multiSelections,
);
final action = promptBlueprintSummaryAction(io);
switch (action) {
case BlueprintSummaryAction.continueAction:
return ConfiguratorRunSuccess(_toBlueprint(state, lock));
case BlueprintSummaryAction.cancel:
return const ConfiguratorRunCancelled();
case BlueprintSummaryAction.restart:
state = _ConfiguratorState(
projectName: initialProjectName ?? 'my_app',
);
index = 0;
case BlueprintSummaryAction.editSection:
index = promptEditSectionNumber(io, configuratorSections.length - 1);
}
continue;
}
io.writeLine('');
io.writeLine(
'=== Section ${index + 1}/${configuratorSections.length}: ${section.title} ===',
);
final directive = section.capabilityIds.isNotEmpty
? _runGroupedCatalogSection(
io,
section,
state,
catalog,
isRoutingSkipValid,
)
: section.isCatalogDriven
? _runCatalogSection(io, section, state, catalog, isRoutingSkipValid)
: _runFixedSection(io, section, state);
switch (directive) {
case _Directive.advance:
index++;
// Re-run compatibility feedback after any change that can affect it
// (FR-041) — every catalog-driven section is exactly such a change.
if (section.isCatalogDriven) {
_printCompatibilityFeedback(
io,
state,
catalog,
lock,
flutterVersion,
dartVersion,
);
}
case _Directive.back:
index = index == 0 ? 0 : index - 1;
case _Directive.restart:
state = _ConfiguratorState(projectName: initialProjectName ?? 'my_app');
index = 0;
case _Directive.cancel:
return const ConfiguratorRunCancelled();
}
}
}