runConfigurator function

Future<ConfiguratorRunResult> runConfigurator(
  1. WizardIo io, {
  2. required Catalog catalog,
  3. required RecipeLock lock,
  4. String? initialProjectName,
  5. bool isRoutingSkipValid() = _routingSkipNotYetProven,
  6. String? flutterVersion,
  7. String? dartVersion,
})

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();
    }
  }
}