generateWidgetReCreator function

String? generateWidgetReCreator(
  1. String className,
  2. ClassInfo info,
  3. List<String> innerTypes,
  4. Map<String, ClassInfo> globalClassLookup,
  5. void warn(
    1. String
    ),
)

Emits a D4.registerGenericTypeWrapper re-creator block for a single type-parameter immutable widget className (recreatorClasses).

Some Flutter widgets (DropdownMenuItem<T>, DropdownMenuEntry<T>, ButtonSegment<T>) cannot be wrapped by a $Relaxed subclass — they are immutable and drive complex rendering. Instead they are RE-CONSTRUCTED with the correct <T> by reading each constructor parameter back from its same-named instance getter. This replaces the hand-written re-creators in d4rt_runtime_registrations.dart.

The emitted block is 2-space-indented, ready to drop inside registerRelaxers(). The switch has a leading dynamic/Object/Object? arm (no value cast), one arm per innerTypes entry (the single type-param constructor parameter cast to that type), and a _ => null fallthrough.

Returns null (and calls warn) when the class cannot be safely re-created: not exactly one type parameter, no default constructor, a constructor parameter without a matching instance getter, more than one parameter referencing the type parameter, or a parameter that references the type parameter in a non-bare form (e.g. List<T>).

Implementation

String? generateWidgetReCreator(
  String className,
  ClassInfo info,
  List<String> innerTypes,
  Map<String, ClassInfo> globalClassLookup,
  void Function(String) warn,
) {
  if (info.typeParameters.length != 1) {
    warn(
      'Re-creator $className: expected exactly 1 type parameter, found '
      '${info.typeParameters.length} — skipping',
    );
    return null;
  }
  final typeParam = info.typeParameters.keys.first;

  ConstructorInfo? ctor;
  for (final c in info.constructors) {
    if (c.name == null) {
      ctor = c;
      break;
    }
  }
  if (ctor == null) {
    warn('Re-creator $className: no default (unnamed) constructor — skipping');
    return null;
  }

  final getterNames = info
      .allInstanceGetters(globalClassLookup)
      .map((m) => m.name)
      .toSet();

  String? typeParamField;
  var typeParamNullable = false;
  for (final p in ctor.parameters) {
    if (!getterNames.contains(p.name)) {
      warn(
        'Re-creator $className: constructor parameter "${p.name}" has no '
        'matching instance getter — skipping',
      );
      return null;
    }
    final stripped = _stripNullableSuffix(p.type).trim();
    if (stripped == typeParam) {
      if (typeParamField != null) {
        warn(
          'Re-creator $className: multiple parameters reference type '
          'parameter $typeParam — skipping',
        );
        return null;
      }
      typeParamField = p.name;
      typeParamNullable = p.type.trim().endsWith('?');
    } else if (_referencesTypeParam(p.type, typeParam)) {
      warn(
        'Re-creator $className: parameter "${p.name}" references type '
        'parameter $typeParam in a non-bare form (${p.type}) — skipping',
      );
      return null;
    }
  }

  final b = StringBuffer();
  b.writeln("  D4.registerGenericTypeWrapper('$className', (");
  b.writeln('    Object value,');
  b.writeln('    String innerTypeArg,');
  b.writeln('  ) {');
  b.writeln('    if (value is! $className) return null;');
  b.writeln('    final v = value;');
  b.writeln('    return switch (innerTypeArg) {');
  _writeReCreatorArm(
    b,
    className,
    'dynamic',
    "'dynamic' || 'Object' || 'Object?'",
    ctor.parameters,
    typeParamField,
    null,
  );
  for (final inner in innerTypes) {
    _writeReCreatorArm(
      b,
      className,
      inner,
      "'$inner'",
      ctor.parameters,
      typeParamField,
      typeParamNullable ? '$inner?' : inner,
    );
  }
  b.writeln('      _ => null,');
  b.writeln('    };');
  b.writeln('  });');
  return b.toString();
}