Skip to content

Inflaters

Inflaters are responsible for dynamically constructing Flutter widgets from XML markup at runtime. They parse attributes and child elements, then generate the corresponding widget instances.

For example, this XML:

<Container height="50" width="50">
    <Text data="Hello world!"/>
</Container>

constructs the following widgets:

Container(
height: 50,
width: 50,
child: Text("Hello world!"),
)

You can create inflaters for any class with a public constructor — not just widgets. For example, BoxDecoration and TextStyle are helper classes that style widgets, and they work the same way.

Defining an Inflater Spec

Create an inflater spec file (e.g., lib/xwidget/inflater_spec.dart):

import 'package:flutter/material.dart';

// Best Practice: Keep declarations in alphabetical order. It makes it much
// easier to quickly determine what has been added and what is missing.

const inflaters = [
  AppBar,
  Center,
  Column,
  Container,
  FloatingActionButton,
  Icon,
  MaterialApp,
  Padding,
  Row,
  Scaffold,
  SizedBox,
  Text,
  TextStyle,
  ThemeData,
];

Then reference it in xwidget_config.yaml:

inflaters:
  sources: [ "lib/xwidget/inflater_spec.dart" ]

How It Works

For each class in the inflaters list, the generator:

  1. Analyzes the class constructors using the Dart analyzer to discover all parameters.
  2. Generates an Inflater subclass that maps XML attributes to constructor arguments.
  3. Generates a parseAttribute() method that converts string attribute values to the correct Dart types using configured parsers.
  4. Generates a registerXWidgetInflaters() function that registers all inflaters with XWidget.
  5. Generates an XSD schema element for each widget with its attributes, enabling IDE code completion in XML fragments.

What to Include

Important

Only specify widgets that you actually use in your UI. Specifying unused widgets will bloat your app size because the generated inflater holds a static reference to each widget's constructor, which prevents tree-shaking from eliminating it.

Include:

  • Flutter widgets you reference in XML fragments (e.g., Scaffold, Column, Text)
  • Non-widget classes used as attribute values (e.g., TextStyle, ThemeData, EdgeInsets)
  • Custom widgets annotated with @InflaterDef

Do not include:

  • Widgets only used in Dart code (they don't need inflaters)
  • Utility classes, mixins, or abstract classes without constructors

Multiple Spec Files

You can split specifications across multiple files:

inflaters:
  sources: [
    "lib/xwidget/inflater_spec.dart",
    "lib/xwidget/inflater_spec_material.dart",
    "lib/xwidget/inflater_spec_custom.dart",
  ]

Include Files

Include files let you inject custom Dart code directly into the generated inflaters output. This is useful for helper functions or custom parsers that inflaters reference:

inflaters:
  includes: [ "lib/xwidget/inflater_spec_includes.dart" ]

The contents of include files (excluding import statements) are copied verbatim into the generated output.

Constructor Exclusions

Exclude specific constructor arguments that should not be settable from XML:

inflaters:
  constructor_exclusions: [
    "CachedNetworkImage:imageRenderMethodForWeb",
    "MyWidget:internalCallback",
  ]

The format is ClassName:argumentName. Use * for the class name to match any class (e.g., *:debugLabel excludes debugLabel from all widgets).

Constructor Argument Defaults

Override default values for constructor arguments:

inflaters:
  constructor_arg_defaults:
    "Text:data": "XWidgetUtils.joinStrings(text)"
    "WidgetSpan:alignment": "PlaceholderAlignment.middle"
    "*:colorBlendMode": "BlendMode.srcIn"

The format is ClassName:argumentName. Use * for the class name to apply to all classes.

Constructor Argument Parsers

Define how string attribute values are parsed into Dart types. The generator uses these to build the parseAttribute() method for each inflater:

inflaters:
  constructor_arg_parsers:
    # Parse by type name
    "bool": "parseBool(value)"
    "double": "parseDouble(value)"
    "int": "parseInt(value)"
    "Color": "parseColor(value)"
    "EdgeInsets": "parseEdgeInsets(value)"

    # Parse by attribute name (any class)
    "*:width": "parseDouble(value)"
    "*:height": "parseDouble(value)"

    # Parse by class:attribute
    "MyWidget:customProp": "parseCustomProp(value)"

The lookup priority is:

  1. ClassName:argumentName — most specific
  2. *:argumentName — any class, specific argument
  3. ArgumentType — by type name

Enum types are automatically parsed without configuration.

Writing a Custom Parser

If XWidget's built-in parsers don't cover your type, write your own. A parser is a function that takes a String? value and returns the parsed type:

Alignment? parseAlignment(String? value) {
  if (value != null && value.isNotEmpty) {
    switch (value) {
      case 'topLeft': return Alignment.topLeft;
      case 'topCenter': return Alignment.topCenter;
      case 'topRight': return Alignment.topRight;
      case 'centerLeft': return Alignment.centerLeft;
      case 'center': return Alignment.center;
      case 'centerRight': return Alignment.centerRight;
      case 'bottomLeft': return Alignment.bottomLeft;
      case 'bottomCenter': return Alignment.bottomCenter;
      case 'bottomRight': return Alignment.bottomRight;
      default: throw Exception("Invalid alignment value: $value");
    }
  }
  return null;
}

Once you've created your parser:

  1. Register it in xwidget_config.yaml under constructor_arg_parsers.
  2. Add the import for the Dart file containing your parser under imports, or place your parser in an include file to have it copied directly into the generated output.

Additional Imports

Sometimes the generator cannot resolve all required imports automatically (e.g., for types used in default values). Add them manually:

inflaters:
   imports: [
      "dart:ui",
      "package:flutter/foundation.dart",
      "package:flutter/gestures.dart",
   ]