coerceList<T> static method
Coerce a List from D4rt to a typed List.
D4rt creates List<Object?> when evaluating list literals, even if all
elements are of the same type. This function coerces the list to the
expected type by casting each element.
Implementation
static List<T> coerceList<T>(Object? arg, String paramName) {
if (arg == null) {
throw ArgumentD4rtException(
'Invalid parameter "$paramName": expected List<$T>, got null',
);
}
// Handle BridgedInstance wrapping
final value = arg is BridgedInstance ? arg.nativeObject : arg;
if (value is! List) {
throw ArgumentD4rtException(
'Invalid parameter "$paramName": expected List<$T>, got ${value.runtimeType}',
);
}
// If already the correct type, return as-is
if (value is List<T>) {
return value;
}
// GEN-080: when T is non-nullable, drop null elements before coercion.
// This matches Dart's collection-if semantics (`if (false) widget` adds
// nothing to the resulting list) and prevents the interpreter — which
// is more lenient than the analyzer about null leaking into typed
// lists — from blowing up with `type 'Null' is not a subtype of type X`
// on bridge constructors that take `List<T>` for non-nullable T.
final iterable = (null is T) ? value : value.where((e) => e != null);
// Coerce each element to the expected type
try {
return iterable.map<T>((e) {
if (e is BridgedInstance) {
final native = e.nativeObject;
if (native is T) return native as T;
// GEN-079: Try wrapper resolution before failing the cast.
// e.g., TweenSequenceItem<dynamic> → $RelaxedTweenSequenceItem<double>
final wrapped = _tryGenericWrapperResolution<T>(native);
if (wrapped != null) return wrapped;
return native as T;
}
if (e is BridgedEnumValue) {
return e.nativeValue as T;
}
// RC-1: InterpretedInstance element unwrapping.
// When a D4rt list contains interpreted widgets/objects that extend
// a bridged type, unwrap via bridgedSuperObject.
if (e is InterpretedInstance) {
if (e.bridgedSuperObject is T) {
return e.bridgedSuperObject as T;
}
// RC-1: Try interface proxy for elements that implement a bridged interface
final effectiveVisitor = _activeVisitor;
if (_interfaceProxies.isNotEmpty && effectiveVisitor != null) {
final proxy = tryCreateInterfaceProxyWithVisitor<T>(
e,
effectiveVisitor,
);
if (proxy != null) return proxy;
}
}
// INTER-003c: int→double element promotion in lists
if (_isDoubleType<T>() && e is int) {
return e.toDouble() as T;
}
// GEN-079: Generic wrapper resolution for list elements.
// When T is a parameterized generic (e.g., TweenSequenceItem<double>)
// and the element has the right base type but wrong type args
// (e.g., TweenSequenceItem<dynamic>), use registered wrapper factories.
final unwrappedElem = e is BridgedInstance ? e.nativeObject : e;
final wrapped = _tryGenericWrapperResolution<T>(unwrappedElem);
if (wrapped != null) return wrapped;
return e as T;
}).toList();
} catch (e) {
throw ArgumentD4rtException(
'Invalid parameter "$paramName": cannot convert List to List<$T> - $e',
);
}
}