FAQ
Architecture & Design
Why does XWidget use XML instead of JSON?
XML was specifically designed for markup languages and document structure — it's the foundation of HTML, Android layouts, iOS XIBs, and XAML. JSON was not designed for UI hierarchies. While excellent for data interchange, it quickly becomes verbose and difficult to read when representing nested UI structures.
Compare this simple XML snippet:
vs the JSON equivalent:
XML gives you more concise code, natural visual hierarchy, support for comments, and decades of mature tooling. It's simply the right tool for UI markup.
How does XWidget handle Flutter API updates?
XWidget generates inflaters directly from Flutter's widget definitions, so the widgets in your inflater spec always match the Flutter version you build against. When Flutter adds properties to a widget you use — or you add a new widget to your spec — regenerate and the changes flow through:
There's no waiting for XWidget to catch up with Flutter releases: the generator reads the widget signatures from your own Flutter SDK. Only widgets listed in your spec are generated — see Can XWidget support custom widgets? below.
Can XWidget support custom widgets?
Yes. Add your custom widget to an inflater spec and regenerate:
const inflaters = [
// Flutter widgets
Column,
Text,
// Your custom widget
MyCustomWidget,
// Third-party package widgets
CachedNetworkImage,
];
Then use them in XML just like Flutter's built-in widgets:
This works for any Flutter widget, including third-party package widgets. See Inflaters for details.
Does XWidget work with all Flutter platforms?
Yes. Since XWidget generates native Flutter widgets, your fragments work everywhere Flutter works — iOS, Android, Web, Windows, macOS, and Linux.
Why is registry.g.dart always regenerated?
The registry reflects which generated files exist on disk and what your config says
about paths. Even when you pass --only inflaters, the registry may need to be
updated to match the rest of the state, so it regenerates unconditionally. The file
is typically under 30 lines, so regeneration is fast. See Registry.
Can I register components manually?
Yes. The register parameter on XWidget.initialize() accepts any function, so
you can provide your own registration callback:
await XWidget.initialize(
register: () {
XWidget.config = const XWidgetConfig(
fragmentsPath: 'resources/fragments',
valuesPath: 'resources/values',
);
registerXWidgetIcons();
registerXWidgetInflaters();
registerXWidgetControllers();
},
);
This is useful when you need fine-grained control — for example, registering a
different set of components based on a feature flag or build configuration. You'll
also need to set XWidget.config yourself if you want paths to come from
xwidget_config.yaml. In most setups, prefer register: registerXWidgetComponents.
Development Workflow
How do I use XWidget fragments in my app?
Inflate a fragment by name with a Dependencies object:
@override
Widget build(BuildContext context) {
return XWidget.inflateFragment("home", Dependencies());
}
Fragments are XML files stored in your assets folder. XWidget parses them at runtime and creates the corresponding Flutter widgets. See Fragments for details.
Can I mix XWidget fragments with regular Flutter widgets?
Yes. XWidget fragments produce standard Flutter widgets, so you can use them anywhere in your widget tree:
Column(
children: [
Text('Native Flutter widget'),
XWidget.inflateFragment("my_fragment", Dependencies()),
ElevatedButton(onPressed: () {}, child: Text('Native button')),
],
)
This makes it easy to adopt XWidget incrementally — use it for screens that benefit from XML-based layouts, keep other screens native. See Start Small, Scale Up.
Does XWidget work with hot reload?
Yes. When you edit a fragment or value XML file during a Flutter debug session, the XWidget IDE plugin pushes the change to the running app via a debug service extension and the resource bundle updates in place — no app restart, no loss of state.
This requires one of the XWidget IDE plugins:
See Hot Reload for details.
How do I test XWidget fragments?
Test them exactly like native Flutter widgets:
testWidgets('home screen test', (tester) async {
await XWidget.initialize(register: registerXWidgetComponents);
await tester.pumpWidget(
MaterialApp(
home: XWidget.inflateFragment("home", Dependencies()) as Widget,
),
);
expect(find.text('Welcome'), findsOneWidget);
});
For test fixtures, pass a TestAssetBundle to an explicit LocalResources:
await XWidget.initialize(
register: registerXWidgetComponents,
resources: LocalResources(
fragmentsPath: 'test/fixtures/fragments',
valuesPath: 'test/fixtures/values',
assetBundle: myTestAssetBundle,
),
);
Can fragments reference other fragments?
Yes. Use the <fragment> tag to include other fragments:
<Scaffold>
<AppBar for="appBar" title="Home"/>
<Column for="body">
<fragment name="header"/>
<fragment name="content"/>
<fragment name="footer"/>
</Column>
</Scaffold>
This enables composition and reusability of UI components. See Nesting Fragments.
How do I handle dynamic data in fragments?
Pass data through a Dependencies object:
final deps = Dependencies();
deps.setValue("product", product);
deps.setValue("user", user);
XWidget.inflateFragment("product_details", deps);
Then reference values in XML using the expression language:
See Dependencies and Expression Language.
Can I conditionally show/hide widgets?
Yes, using the visible attribute on the <fragment> tag or control flow tags:
<fragment name="premium_features" visible="${user.isPremium}"/>
<if test="${!user.isPremium}">
<ElevatedButton onPressed="${upgrade}">
<Text>Upgrade to Premium</Text>
</ElevatedButton>
</if>
See If/Else for more about conditional rendering.
Does XWidget support lists and iterations?
Yes, use the <forEach> tag:
<Column>
<forEach var="item" items="${items}">
<Card>
<Text data="${item.name}"/>
</Card>
</forEach>
</Column>
See forEach.
Can I use complex expressions in XML?
Yes. XWidget supports a full expression language with operators, 60+ built-in functions, and custom logic:
<Text data="${user.firstName + ' ' + user.lastName}"/>
<Text visible="${items.length > 0}" data="Found ${items.length} items"/>
<Container color="${isActive ? toColor('#00FF00') : toColor('#FF0000')}">
<Text data="${toString(price * quantity)}"/>
</Container>
See Expression Language for the full reference.
Performance
Is there a performance penalty for using XWidget?
The XML parsing happens once when the fragment loads, then you have native Flutter widgets — the same widgets you'd create in Dart code. There's a small one-time parsing cost, but runtime performance is identical to hand-written Flutter code because the resulting widget tree is identical.
XWidget also caches parsed XML documents by default to avoid re-parsing on subsequent inflations.
Does XWidget support all Flutter widgets?
Any Flutter widget can be used — Material, Cupertino, third-party package widgets, or your own. Add the widget class to your inflater spec and regenerate; XWidget analyzes its constructor and generates an inflater that exposes it as an XML element.
When Flutter adds new widgets, add them to your spec and regenerate to gain support.
Cloud & Analytics
What is XWidget Cloud?
XWidget Cloud is a deployment and analytics platform that lets you push UI updates to your users without going through app store review cycles. You deploy bundles of XML fragments and value resources, and your app downloads them at runtime.
See Cloud Overview.
Do I need XWidget Cloud to use XWidget?
No. XWidget works entirely with local assets. Cloud is optional — you can add it later when you're ready for over-the-air updates and analytics.
How do I enable analytics?
Pass a CloudResources or LocalResources.withAnalytics instance to
XWidget.initialize():
// With cloud-delivered UI
await XWidget.initialize(
register: registerXWidgetComponents,
resources: CloudResources(
projectKey: '<your-project-key>',
storageKey: '<your-storage-key>',
channel: 'production',
version: '1.0.0',
),
);
// Or with UI bundled in the app binary
await XWidget.initialize(
register: registerXWidgetComponents,
resources: LocalResources.withAnalytics(
projectKey: '<your-project-key>',
version: '1.0.0',
),
);
Analytics are collected automatically — no manual instrumentation required. See Analytics.
How do I use XWidget Cloud analytics without cloud-delivered UI?
Use LocalResources.withAnalytics. Your UI still loads from the asset bundle, but
renders, errors, and navigation are reported to XWidget Cloud. The channel is 'local'.
See LocalResources.
Can I self-host the cloud bundle server?
Yes — but don't try to point CloudResources at your own server. Extend Resources
directly instead. CloudResources is a reference implementation of download / verify
/ cache / fallback; copy it as a starting point and adapt the download step for your
hosting environment. See CloudResources.
What analytics does XWidget collect?
XWidget automatically tracks fragment renders, bundle downloads, errors, and navigation transitions. All data is aggregated on-device and flushed to the server periodically. You can query it via the CLI:
xc analytics renders -c production -r 30
xc analytics downloads
xc analytics errors
xc analytics transitions -f /home
See Analytics Overview.
Migration
Can I migrate an existing Flutter app to XWidget incrementally?
Yes. Start by converting one screen to an XWidget fragment, test it, then gradually convert more screens. You can have a hybrid app where some screens are native Flutter and others are XWidget fragments. There's no all-or-nothing decision.
How do I convert existing Flutter widgets to XWidget XML?
The conversion is straightforward since XML mirrors Flutter's widget structure:
Flutter:
Scaffold(
appBar: AppBar(title: Text('Home')),
body: Column(
children: [
Text('Welcome!'),
ElevatedButton(
onPressed: () => XRouter.goTo('/profile'),
child: Text('View Profile'),
),
],
),
)
XWidget:
<Scaffold>
<AppBar for="appBar" title="Home"/>
<Column for="body">
<Text>Welcome!</Text>
<ElevatedButton onPressed="${routeTo('/profile')}">
<Text>View Profile</Text>
</ElevatedButton>
</Column>
</Scaffold>
The structure is nearly identical — just translate the syntax. See Quick Start to get started.