XML layout for flutter. Layout your UI via xml at runtime. and support extending any customer widget. Here is a online preview.
write xml layout file like:
xml
<Text mainAxisAlignment="center">
<for count="6">
<Text>$item, You have pushed the button this many times:</Text>
</for>
<Text id="text-id">
<attr:style>
<TextStyle color="red"/>
</attr:style>
$counter
</Text>
</Column>
dart
XMLLayout(
temp: snapshot.data,
objects: {
"counter": _counter
},
)
In this case
$counter
for passing argument to the layout.
id
attribute for selecting the widget or state.
XMLLayoutState state = ...;
// find the key of the Text
GlobalKey key = state.find('text-id');
register
- description: Register a constructor. It could convert a xml element to target object.
/**
* Register a constructor
*
* xml:
* <MyClass width="10" height="10" />
*
*/
XMLLayout.register('MyClass', (node, key) {
return MyClass(
key: key,
child: node.child<Widget>(),
width: node.s<double>("width"),
height: node.s<double>("height"),
);
});
registerEnum
- description: A shortcat to register a enum class. It could convert a attribute to the enum.
/**
* Register a enum type
*
* xml:
* <Text textAlign="center">str</Text>
*/
XMLLayout.registerEnum(TextAlign.values);
registerInline(Type type, String name, bool field, InlineItemConstructor constructor)
- description: Register a constructor which could convert a attribute to target type.
- arguments:
field
this constructor is for a static field or a constructor.
/**
* <Text fontWeight="w200">str</Text>
*/
XmlLayout.registerInline(FontWeight, "w200", true, (node, method) {
return FontWeight.w200;
});
/**
* <Text textHeightBehavior="fromEncoded(20)">str</Text>
*/
XmlLayout.registerInline(TextHeightBehavior, "fromEncoded", false,
(node, method) {
return TextHeightBehavior.fromEncoded(int.tryParse(method[0]));
});
node.s<T>("name")
, node.attribute<T>("name")
convert subnode to target type
node.t<T>()
, node.convert<T>()
convert this node to target type
node.v<T>("value")
, node.value<T>("value")
convert text to target type
<ListView.separated itemCount="$itemCount">
<attr:itemBuilder>
<Function returnType="Widget">
<!-- get arguments of function via args -->
<SetArgument return="index" argument="${args[1]}"/>
<Call function="$getItem" return="itemData">
<!-- pass argument to getItem function -->
<Argument value="$index"/>
</Call>
<!-- The last element of Function tag would be the final result -->
<Text>${itemData.title}</Text>
</Function>
</attr:itemBuilder>
</ListView.separated>
<Function>
<Script>
set("index", ${args[1]})
set("itemData", getItem($index))
</Script>
<!-- same as -->
<SetArgument return="index" argument="${args[1]}"/>
<Call function="$getItem" return="itemData">
<Argument value="$index"/>
</Call>
<!-- end(same as) -->
<Text>${itemData.title}</Text>
</Function>
Method
could be registerd via XmlLayout.registerInlineMethod
, and
can be used in a Xml attribute or Script tag.
Default methods:
isEmpty(a)
=> a.isEmpty()isNotEmpty(a)
=> a.isNotEmpty()equal(a, b, ...)
=> a == b ...net(a, b)
=> a != bmod(a, b)
=> a % bdiv(a, b)
=> a / bset(name, a)
=> env[name] = anot(a)
=> !alt(a, b)
=> a < bnlt(a, b)
=> a >= bgt(a, b)
=> a > bngt(a, b)
=> a <= bplus(a, b, ...)
=> a + b ...minus(a, b, ...)
=> a - b ...multiply(a, b, ...)
=> a * b ...divide(a, b, ...)
=> a / b ...
A util to control the rendering logic. like:
<for count="$counter">
<Text>$item, You have pushed the button this many times:</Text>
<if candidate="equal(1, mod($item, 2))">
<Text>Test text</Text>
</if>
</for>
if
tag is a if control flow.- attributes:
candidate
a Boolean value, if true the children would be rendered, otherwise not be rendered.
- attributes:
else
tag is a else control flow, could be placed afterif
or anotherelse
tag.- attributes:
candidate
same asif
tag.
- attributes:
for
tag is a loop control flow.- attributes:
array
a List value, iterates over array elements, each element for children one time.count
a Integer value, iterates children the value times. It is mutually exclusive with thearray
attribute.item
a String value, the name of each element, default isitem
.index
a String value, the name of the index of element, default isindex
.
- attributes:
You can write a script to generate the constructor code.
In the example test.dart
is the builder script.
entry_name
- default:
types
- type:
List<Type>
- description: types in this list which will be processed.
- default:
collections_name
- default:
collections
- type:
List<Collection>
- description: Collection type is used to process the collection class, such as:
Colors
andIcons
.
- default:
ps: Colors
and Icons
is preprocessed just import it via:
import 'package:xml_layout/types/colors.dart' as colors;
import 'package:xml_layout/types/icons.dart' as icons;
// ...
colors.register();
icons.register();
coverts_name
- default:
converts
- type:
Map<String, String>
- description: Every import uri will be test by the key value pair in this variable. if a import source uri is start with the key then it will be convert to the value.
- default:
imports_name
- default:
imports
- type:
List<String>
- description: Extension import uris which will be write to the generated code.
- default:
lib/test.dart
import 'package:flutter/material.dart';
const List<Type> types = [
Text,
];
build.yaml
targets:
$default:
builders:
xml_layout:
generate_for:
- lib/test.dart
Create the files above, then just run flutter pub run build_runner build
.
And you will get the generated code in lib/test.xml_layout.dart
.