toXmlString method
String
toXmlString({
- int indent = 0,
- required int boundsPrecision,
- Rect? parentBounds,
- BoxConstraints? parentConstraints,
Implementation
String toXmlString({
int indent = 0,
required int boundsPrecision,
Rect? parentBounds,
BoxConstraints? parentConstraints,
}) {
final indentStr = ' ' * indent;
final tag = widget.runtimeType
.toString()
.replaceAll('<', '-')
.replaceAll('>', '')
.replaceAll(',', '-')
.replaceAll(' ', '');
// associate properties with their values (opt-in selection)
final diagnostics = widget.toDiagnosticsNode().getProperties();
final allowedNames = _allowedPropertyNames(widget, diagnostics);
final Map<String, dynamic> props = {};
// Process properties, handling BoxDecoration specially
for (final p in diagnostics) {
if (p.name != null && p.value != null && allowedNames.contains(p.name)) {
// Check if this is a BoxDecoration property (bg or decoration)
if ((p.name == 'bg' || p.name == 'decoration') &&
p.value is BoxDecoration) {
// Flatten BoxDecoration properties directly onto the element
final decoration = p.value as BoxDecoration;
final flattenedProps = _serializeBoxDecoration(decoration);
// Sanitize the flattened property values
for (final entry in flattenedProps.entries) {
props[entry.key] = _sanitizeAttributeValue(entry.value);
}
} else {
// Regular property handling
props[p.name!] = _sanitizeAttributeValue(
_getPropertyValueString(p),
);
}
}
}
// Determine if bounds should be included:
// - Always include for root widget (parentBounds is null)
// - Include if bounds differ from parent bounds
final shouldIncludeBounds = bounds != null &&
(parentBounds == null ||
!_boundsMatchParent(bounds!, parentBounds, boundsPrecision));
// Determine if constraints should be included:
// - Always include for root widget (parentConstraints is null)
// - Include if constraints differ from parent constraints
final boxConstraints =
constraints is BoxConstraints ? (constraints as BoxConstraints) : null;
final shouldIncludeConstraints = boxConstraints != null &&
(parentConstraints == null ||
!_constraintsMatchParent(boxConstraints, parentConstraints));
final attrs = [
...props.entries.map((e) {
// Height and line height are conflicting properties
if (e.key == "height") {
return ' lineHeight="${e.value}"';
}
return ' ${e.key}="${e.value}"';
}),
if (shouldIncludeBounds) ...[
if (!props.containsKey("left"))
' left="${bounds!.left.toStringAsFixed(boundsPrecision)}"',
if (!props.containsKey("top"))
' top="${bounds!.top.toStringAsFixed(boundsPrecision)}"',
' width="${bounds!.width.toStringAsFixed(boundsPrecision)}"',
' height="${bounds!.height.toStringAsFixed(boundsPrecision)}"',
],
if (shouldIncludeConstraints) ...[
' maxHeight="${boxConstraints.maxHeight.toStringAsFixed(boundsPrecision)}"',
' maxWidth="${boxConstraints.maxWidth.toStringAsFixed(boundsPrecision)}"',
' minHeight="${boxConstraints.minHeight.toStringAsFixed(boundsPrecision)}"',
' minWidth="${boxConstraints.minWidth.toStringAsFixed(boundsPrecision)}"',
],
if (widget is RichText) ...[
' text="${_sanitizeAttributeValue((widget as RichText).text.toPlainText())}"',
' color="${_colorToHex((widget as RichText).text.style?.color)}"',
' family="${(widget as RichText).text.style?.fontFamily ?? 'null'}"',
' size="${(widget as RichText).text.style?.fontSize ?? 'null'}"',
' weight="${(widget as RichText).text.style?.fontWeight?.toString() ?? 'null'}"',
' lineHeight="${(widget as RichText).text.style?.height?.toString() ?? 'null'}"',
],
if (widget is Text) ...[
' text="${_sanitizeAttributeValue((widget as Text).data ?? (widget as Text).textSpan?.toPlainText() ?? '')}"',
' overflow="${(widget as Text).overflow?.toString() ?? 'null'}"',
' alignment="${(widget as Text).textAlign?.toString() ?? 'null'}"',
' weight="${(widget as Text).style?.fontWeight?.toString() ?? 'null'}"',
' lineHeight="${(widget as Text).style?.height?.toString() ?? 'null'}"',
],
// Handle LdText widget - check by runtimeType string to avoid import dependency
if (widget.runtimeType.toString() == 'LdText')
...(() {
try {
final ldText = widget as dynamic;
return [
' type="${ldText.type?.toString() ?? 'null'}"',
' size="${ldText.size?.toString() ?? 'null'}"',
];
} catch (_) {
return <String>[];
}
})(),
].join('');
final content = children.isEmpty
? ''
: [
'',
...children.map((child) => child.toXmlString(
indent: indent + 1,
boundsPrecision: boundsPrecision,
parentBounds: bounds,
parentConstraints: boxConstraints)),
'',
].join('\n') +
indentStr;
final slash = children.isEmpty ? ' /' : '';
final closingTag = children.isEmpty ? '' : '</$tag>';
final result = '$indentStr<$tag$attrs$slash>$content$closingTag'
// replace UID hash codes with a generic placeholder
.replaceAllMapped(
RegExp(r'([a-zA-Z_>]+)#[0-9a-fA-F]+'),
(match) => '${match.group(1)}#HASH',
);
return result;
}