A minimal compiler from readable XML to Flutter. Designed as an easy way to get into Flutter, not as a replacement.
- Flutter installed
- Python installed (should come with most OSes)
- Navigate to the directory you want your project in
- run
flutter create src
- copy
processor.py
into the directory - create your
.xml
files - run
python processor.py
to generate your source files - If you don't already have
flutter
running:
cd src/
flutter run lib/main.dart
If you do, just go to the console window where you have it running and hit r
to reload or R
to restart.
(Note: many features aren't implemented yet and the order in which features are added is very arbitrary, so if any of you guys want to see a feature implemented feel free to open an issue and i'll try to prioritize it)
Used to specify the entry point of the application:
<!--main.xml-->
<Main title="my app">
<Text>I love Flap!</Text>
</Main>
Used to create a reusable widget:
<!--TestWidget.xml-->
<Widget name="Test">
<Center>
<Container bg="{color: bgcolor}">
<Padding all="8"/>
<Text size="20">
{str: text} and more
</Text>
</Container>
</Center>
</Widget>
Text in brackets specifies parameters in the form {type: name}
, and the widget can be used like so:
<Link src="TestWidget" />
...
<Test text="some text" bgcolor="color: blue"/>
Parameter names cannot be style names (e.g. bg
). Note that any parameter type other than str
must be specified (note "some text"
vs "color: blue"
).
A note about the bg
argument: you only need to specify color: <color>
if you're passing the color as a parameter. If the background color is fixed, you should write bg: <color>
. (Specifying a type will make flap think you're referencing a variable)
Widgets are by default stateless, but a State
can be introduced with the State
tag (who would've guessed?):
<Widget name="StfulDemo">
<State content="num: {initial}"/>
<Button>
<Press>
<Change>
content++;
</Change>
</Press>
<Text>$content</Text>
</Button>
</Widget>
By specifying num: {initial}
, we not only set content
to an integer but also allow an initial value to be passed in in the form a parameter. Note that all parameters MUST be passed in through the State
tag or they will not be processed. We can reference the content
variable within the Text
tag by putting a $
sign before the variable name. setState
can be called with the Change
tag.
<Container bg="green">
<Margin all="8" />
<Padding all="8" />
<Text>The last element is always the child.</Text>
<Container>
A simple wrapper that allows you to style its child (the last element). Accepts Margin
and Padding
elements to add spacing, and the bg
attribute to set the background color.
<Navbar>
<Text>Home</Text>
<Text>About</Text>
</Navbar>
Creates an appBar
with the specified children.
<Button>
<Press>
print('Pressed');
print('Check formatting');
</Press>
<Text>{str: buttontext}</Text>
</Button>
The Press
element sets on onPressed
for the Button
: its contents must be written in dart
. If Press
is not specified the Button
will be disabled. The child
tag can be either Text
or Icon
.
<Text size="20" color="white">White text: font size 20</Text>
Plain text: accepts size
and color
attributes.
<Icon color="red" size="100">delete</Icon>
Pulls an icon from the Flutter Icons
class.
<Row>
<Text>one</Text>
<Text>two</Text>
<Text>three</Text>
</Row>
Horizontal/vertical container.
If you want to contribute to this project, here's a few tips to get you started:
processor.py
is the only source file that should be modified (i.e. everything is crammed into one file)- The
processor
takes advantage of thepython
reflection system to make code refactoring easier, but this also means that all functions must be named asprocess<Widget>
(e.g.processContainer
,processMargin
,processMain
, etc.) - Each of said functions should return the generated source code without extra indents (i.e. base indentation should always be 0, but nested elements can and should be indented), newlines, commas or semicolons.
Example function and breakdown:
def processRow(xml): # all functions accept am xml argument
children = []
for child in xml: # loops through all children
if child.tag in styleComponents: continue # ignore style for now
children.append(process_xml(child)) # add the string representation to the list of children
fchildren = textwrap.indent(",\n".join(children), "\t\t") # indent children properly
return \
f"""Row(
children: [
{fchildren}
]
)""" # return the string representation
If you have any more questions about contribution, let me know by opening an issue.