extractRepeatedWidgetsToComponent method

void extractRepeatedWidgetsToComponent({
  1. required String componentName,
  2. required List<BrownfieldPatternOccurrence> occurrences,
})

Extracts repeated existing widgets into a shared project component and replaces each occurrence with a component instance.

Use this for reuse-first refactors once repeated patterns are known.

Implementation

void extractRepeatedWidgetsToComponent({
  required String componentName,
  required List<BrownfieldPatternOccurrence> occurrences,
}) {
  if (occurrences.length < 2) {
    throw ArgumentError(
      'extractRepeatedWidgetsToComponent(...) requires at least two occurrences.',
    );
  }
  raw((project) {
    final sourceOccurrence = occurrences.first;
    final sourceWidgetClass = _findWidgetClassByName(
      project,
      sourceOccurrence.widgetClassName,
    );
    final sourceSnapshot = _snapshotForWidgetClass(
      project,
      sourceWidgetClass,
    );
    final sourceRef =
        sourceSnapshot.findByName(sourceOccurrence.widgetName).expectOne();
    final sourceNode = findByKey(sourceWidgetClass.node, sourceRef.key);
    if (sourceNode == null) {
      throw StateError(
        'Widget "${sourceOccurrence.widgetName}" was not found on '
        '"${sourceOccurrence.widgetClassName}".',
      );
    }

    if (project_helpers.findComponent(project, name: componentName) == null) {
      project_helpers.addComponent(
        project,
        name: componentName,
        description: 'DSL extracted component $componentName',
        body: UI.container(name: componentName),
      );
    }
    final componentBody =
        sourceNode.type == FFWidgetType.Container
            ? sourceNode.deepCopy()
            : UI.container(child: sourceNode.deepCopy(), name: componentName);
    project_helpers.updateComponentBody(
      project,
      name: componentName,
      body: componentBody,
    );
    final componentClass =
        project_helpers.findComponent(project, name: componentName)!;
    for (final occurrence in occurrences) {
      final widgetClass = _findWidgetClassByName(
        project,
        occurrence.widgetClassName,
      );
      final snapshot = _snapshotForWidgetClass(project, widgetClass);
      final targetRef =
          snapshot.findByName(occurrence.widgetName).expectOne();
      final replacement = UI.component(
        componentClass,
        name: occurrence.widgetName,
      );
      if (!replaceByKey(widgetClass.node, targetRef.key, replacement)) {
        throw StateError(
          'Failed to replace repeated widget "${occurrence.widgetName}" on '
          '"${occurrence.widgetClassName}".',
        );
      }
    }
  });
}