What is XWidget?
XWidget is server-driven UI for Flutter apps.
Ship UI changes as versioned resource bundles instead of waiting on app-store builds and user updates. Layout, copy, styling, navigation, feature surfaces, test variants — it all updates live from XWidget Cloud, while your compiled Flutter app keeps control of the things that should never leave the binary: services, credentials, permissions, business rules, and native integrations.
The short version
XML is the markup. Bundles are the transport. Flutter remains the runtime.
Server-Driven Flutter
Flutter gives developers a fast feedback loop during development. Production is usually slower: even small UI changes can require a new build, app store review, gradual user adoption, and coordination across platforms.
XWidget moves presentation resources out of the app binary and into deployable UI bundles. Your app starts with a registered set of Flutter widgets, controllers, icons, functions, and model types. At runtime, XWidget loads XML fragments and value resources, evaluates expressions, and inflates native Flutter widgets.
That gives you a controlled SDUI surface:
- update UI without an app store release
- deploy to staging before production
- publish known-good revisions to any channel
- run UI experiments, rollouts, and A/B tests from resources
- keep remote UI inside the capabilities compiled into the app
- continue using Flutter widgets, custom widgets, and third-party package widgets
Flutter First
XWidget is designed to work with the Flutter code you already write.
You choose which Flutter classes should be available in XML, and XWidget Builder generates the inflaters and XML schema for those classes. That means your markup can use Flutter SDK widgets, your own app widgets, and widgets from third-party packages.
import 'package:flutter/material.dart';
import 'package:my_app/widgets/revenue_chart.dart';
const inflaters = [
Button,
Column,
Container,
Padding,
RevenueChart,
Row,
Text,
TextStyle,
];
Run the generator, and those classes become XML elements with schema-backed attribute completion:
The generated binding is based on the Flutter and package versions in your app. If your app can compile it, XWidget can expose it.
Markup That Looks Like UI
XWidget fragments are XML documents. XML is used as authored markup because UI is tree-shaped, nested, and readable when the document mirrors the widget tree.
<Controller name="OverviewController" xmlns="https://xwidget.dev/fragments">
<ValueListener varName="overview">
<Column crossAxisAlignment="start">
<Text data="${overview.title}">
<TextStyle for="style" fontSize="28" fontWeight="bold" />
</Text>
<Text data="${overview.subtitle}" />
<Button onPressed="${refreshOverview}">
<Text>Refresh</Text>
</Button>
</Column>
</ValueListener>
</Controller>
With the generated XML schema registered in your IDE, fragments get completion, validation, and inline documentation while you author them.
Fragments can include widgets, resources, expressions, controllers, conditions, loops, callbacks, and nested fragments. They are small enough to review, easy to diff, and natural to deploy as versioned UI resources.
State With Boundaries
Server-driven UI should not mean server-driven everything.
XWidget keeps behavior in Dart and presentation in resources. Controllers load
data, call services, enforce permissions, mutate state, and publish values or
functions into Dependencies. XML listens and reacts.
Use Dependencies for glue values and callbacks. Use Model when data has
shape: API records, page state, forms, validation, type conversion, or nested
objects.
class OverviewController extends Controller {
@override
Future<void> init() async {
dependencies.setValue('overview', OverviewState.loading());
final usage = await api.getUsage();
dependencies.setValue('overview', OverviewState.fromUsage(usage));
}
@override
void bindDependencies() {
dependencies.setValue('refreshOverview', refreshOverview);
}
Future<void> refreshOverview() async {
final usage = await api.getUsage();
dependencies.setValue('overview', OverviewState.fromUsage(usage));
}
}
This boundary keeps production UI flexible without turning remote resources into an unrestricted scripting environment.
Deploy Through Channels
XWidget Cloud treats UI bundles as versioned, immutable deployment artifacts.
A deployment is a bundle of XML fragments and value resources; each deploy of a
version mints a new numbered revision. Channels are named targets such as
development, staging, and production that point at the revision they serve —
publishing moves the pointer.
# Deploy a candidate bundle (mints revision 0 of 1.4.0).
xc cloud deploy -v 1.4.0 -n "New project settings flow"
# Publish it to staging.
xc cloud publish -c staging -v 1.4.0 -r 0
# Inspect what staging is serving.
xc cloud deployment list -c staging
# Publish the same tested revision to production.
xc cloud publish -c production -v 1.4.0 -r 0
Your app resolves the channel and version it should load from remote configuration, feature flags, user cohorts, or a startup config API. That lets you steer beta users, internal testers, rollout groups, and A/B testing cohorts to different deployed UI bundles.
final remoteConfig = await fetchRemoteConfig();
await XWidget.initialize(
register: registerXWidgetComponents,
resources: CloudResources(
projectKey: '<your-project-key>',
storageKey: '<your-storage-key>',
channel: remoteConfig.xwidgetChannel, // i.e. production
version: remoteConfig.xwidgetVersion, // i.e. 1.5.0
),
);
Because channels and versions map to deployed bundles, teams can control what is available remotely while the app decides which users receive which UI.
That gives teams an operational workflow for production UI:
- Author fragments and values.
- Generate bindings and schema.
- Deploy to a non-production channel.
- Test the exact deployed bundle.
- Promote that same version to production.
- Use analytics and error data to see how the UI behaves in the wild.
What Can Change Remotely
XWidget is strongest when you use the remote layer for presentation and flow, while keeping sensitive logic compiled into the app.
| Remote resources | Compiled Flutter app |
|---|---|
| Layout and composition | API clients and credentials |
| Copy and static values | Authentication and authorization |
| Colors and style resources | Business rules and validation |
| Fragment selection | Native platform integrations |
| Navigation structure | Custom widgets and controllers |
| Feature surfaces and experiments | Payment, storage, and permissions |
The app decides what is possible. The deployed resources decide how the allowed surface is presented.
Start Small, Scale Up
XWidget can be introduced gradually. Use it for one screen, one feature area, a settings flow, a marketing surface, or a full application shell. Local resources work well during development and for bundled fallback UI. Cloud resources add deployment, channels, downloads, analytics, and production visibility.
Use XWidget when you want:
- production UI updates without rebuilding the app
- the freedom to use real Flutter and third-party widgets
- readable XML markup for UI documents
- typed Dart controllers for behavior
- structured models for runtime state
- versioned deployments through channels
- automatic render, download, error, and transition analytics
Next Steps
- Quick Start - install XWidget and render your first fragment
- Server-Driven UI - understand the runtime model
- Inflaters - expose Flutter widgets to XML
- Controllers - keep behavior in Dart
- Models - structure runtime state
- Cloud Deployments - deploy and promote UI bundles