Skip to content

Resources

Resources are externalized data that XWidget loads at initialization — either from local assets or from XWidget Cloud. There are two categories: value resources and fragment resources.

Value Resources

Value resources define named constants in XML files stored in your values directory (default: resources/values/). They provide a centralized place to manage strings, colors, and other primitives that your fragments reference.

Defining Values

Create XML files in your values directory. Each file uses a <resources xmlns="https://xwidget.dev/values"> root element containing typed entries:

<!-- resources/values/strings.xml -->
<resources xmlns="https://xwidget.dev/values">
    <string name="app_title">My App</string>
    <string name="welcome_message">Welcome to XWidget!</string>
</resources>
<!-- resources/values/colors.xml -->
<resources xmlns="https://xwidget.dev/values">
    <color name="primarySwatch">#2196F3</color>
    <color name="background">#FFFFFF</color>
    <color name="textDark">#262626</color>
</resources>

Supported Types

Element Dart Type Example
<string> String <string name="title">Hello</string>
<bool> bool <bool name="darkMode">true</bool>
<int> int <int name="maxRetries">3</int>
<double> double <double name="opacity">0.85</double>
<color> Color <color name="primary">#2196F3</color>

Every entry requires a name attribute, which serves as the lookup key.

Accessing Values in Dart

Values are accessed through the Resources singleton using type-specific getters:

final title = Resources.instance.getString("app_title");
final primary = Resources.instance.getColor("primarySwatch");
final retries = Resources.instance.getInt("maxRetries");
final darkMode = Resources.instance.getBool("darkMode");
final opacity = Resources.instance.getDouble("opacity");

If a requested resource does not exist, an exception is thrown.

Accessing Values in Fragments

Value resources are referenced in XML fragments using @ directives. The format is @type/name, where type matches the resource element type and name matches the name attribute in your value resource XML.

Strings

<!-- resources/values/strings.xml -->
<resources xmlns="https://xwidget.dev/values">
    <string name="app_title">My App</string>
    <string name="welcome_message">Welcome to XWidget!</string>
</resources>
<!-- fragment usage -->
<Text data="@string/app_title"/>
<Text data="@string/welcome_message"/>

Colors

<!-- resources/values/colors.xml -->
<resources xmlns="https://xwidget.dev/values">
    <color name="primary">#2196F3</color>
    <color name="background">#FFFFFF</color>
</resources>
<!-- fragment usage -->
<Container color="@color/background">
    <Text data="Hello" color="@color/primary"/>
</Container>

Booleans

<!-- resources/values/flags.xml -->
<resources xmlns="https://xwidget.dev/values">
    <bool name="showWelcome">true</bool>
    <bool name="debugMode">false</bool>
</resources>
<!-- fragment usage -->
<fragment name="welcome_banner" visible="@bool/showWelcome"/>

Integers and Doubles

<!-- resources/values/dimensions.xml -->
<resources xmlns="https://xwidget.dev/values">
    <int name="maxItems">25</int>
    <double name="defaultPadding">16.0</double>
    <double name="borderRadius">8.0</double>
</resources>
<!-- fragment usage -->
<Padding padding="@double/defaultPadding">
    <Container borderRadius="@double/borderRadius"/>
</Padding>

Directives vs. Expressions

The @ directive and the expression language ${} serve different purposes. Directives reference static resource values. Expressions evaluate dynamic data from dependencies. They cannot be combined in the same attribute value.

<!-- Directive — static resource value -->
<Text data="@string/app_title"/>

<!-- Expression — dynamic dependency value -->
<Text data="${user.name}"/>

Use directives for values that are defined at build time and shared across fragments (colors, labels, dimensions, feature flags). Use expressions for runtime data that comes from controllers and dependencies.

Multiple Value Files

You can organize values across multiple XML files. All files in the values directory are loaded and merged at initialization:

resources/values/
├── colors.xml
├── strings.xml
├── dimensions.xml
└── feature_flags.xml

Resource names must be unique within each type across all files.

Fragment Resources

Fragment resources are the XML files that define your UI. They are stored in your fragments directory (default: resources/fragments/).

Directory Structure

resources/fragments/
├── my_app.xml
├── home.xml
├── settings/
│   ├── index.xml
│   └── profile.xml
└── auth/
    ├── login.xml
    └── register.xml

Fragment Resolution

When inflating a fragment by name, XWidget resolves the file using these rules:

  1. If the name ends with .xml, look up the exact path.
  2. Otherwise, try <name>/index.xml first, then <name>.xml.
// Resolves to "home.xml" or "home/index.xml"
XWidget.inflateFragment("home", dependencies);

// Resolves to "settings/profile.xml"
XWidget.inflateFragment("settings/profile", dependencies);

Registering Asset Directories

All fragment directories (including subdirectories) must be registered in pubspec.yaml:

flutter:
  assets:
    - resources/fragments/
    - resources/fragments/settings/
    - resources/fragments/auth/
    - resources/values/

Tip

The xc init command automatically registers resources/fragments/ and resources/values/. You only need to add subdirectories manually.

Custom Resource Bundles

For advanced use cases, you can create custom resource bundle types by extending ResourceBundle. Register them from a custom Resources subclass during load():

class MyCustomResources extends ResourceBundle {
  MyCustomResources() : super('custom');

  @override
  Future<void> loadFromAssetBundle(
    String fileName,
    String resPath,
    String resName,
    String resExt,
    AssetBundle assetBundle,
  ) async {
    // Load and parse your custom resource format
  }

  @override
  void loadFromString(
    String resPath,
    String resName,
    String resExt,
    String content,
  ) {
    // Parse from string (used by cloud resource loading)
  }
}

class MyResources extends LocalResources {
  @override
  Future<void> load() async {
    await super.load();

    final custom = MyCustomResources();
    // Load or populate the custom bundle here, then register it.
    addResourceBundles([custom]);
  }
}

await XWidget.initialize(
  register: registerXWidgetComponents,
  resources: MyResources(),
);

Resources.instance is only available after XWidget.initialize() activates the resource provider, so custom bundles must be registered by the active provider itself. Built-in providers load fragment and value bundles; custom bundle loading is the responsibility of your custom Resources implementation.