Creating widget layouts

Overview

In Flutter, widget trees are built by passing a child or children. Widget trees have a lot of indentation, especially since in Flutter nearly everything is a widget, and composition is preferred to inheritance. This can lead to complex nesting and child parameters.

Flutter-views are optimised for building widget trees. Pug in particular is well suited for creating tree structures and moving parts around.

The following example generates a Dart method FooPage(), which returns a Scaffold with an AppBar, and a centralized greeting message.

The Pug creates the layout, and the main.dart file uses this layout to render the app. This separation between layout and logic is fundamental to using flutter-view.

Pug
generated Dart
main.dart
foo-page(flutter-view :greeting)
scaffold
app-bar(as='appBar')
container(as='title') Foo Page
center(as='body') Hello $greeting!
FooPage({ greeting}) {
return Scaffold(
appBar: AppBar(
title: Container(
child: Text('Foo page'),
),
),
body: Center(
child: Text('Hello $greeting!'),
),
);
}
import 'package:flutter/material.dart';
import 'foo-page.dart';
void main() => runApp(TestApp());
class TestApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test App', home: FooPage(greeting: 'world!')
);
}
}

Adding children to widgets

In a flutter-view, add indented child tags to tags to automatically have them assigned as child or children parameters.

In flutter-view Pug/HTML content, an HTML tag is a method or constructor call, and its parameters are parameters for this method or constructor. The tag children are assigned as the Flutter child/children parameters.

To see how this works, compare the Pug and generated Dart code in this example:

Pug
generated Dart
container
column
row
text(:value='first row!')
row
text(:value='second row!')
row
flat-button
text(:value='Click me!')
return Container(
child: Column(
children: [
Row(
children: [
Text('first row!'),
],
),
Row(
children: [
Text('second row!'),
],
Row(
FlatButton(
child: Text('Click me!'),
)
),
],
),
);

Automatic columns

Flutter-view knows which classes generally need children instead of single child, and automatically creates Columns when you pass multiple children. Also, if you pass text, flutter-view it automatically wraps it in a Text widget as well. This together allows you to be more terse:

Pug
generated Dart
container
row first row!
row second row!
row
flat-button Click me!
return Container(
child: Column(
children: [
Row(
children: [
Text('first row!'),
],
),
Row(
children: [
Text('second row!'),
],
Row(
FlatButton(
child: Text('Click me!'),
)
),
],
),
);

You can override the default wrapper in flutter-view.json.

Calling dart factory constructors

Some Flutter Dart classes may use factory constructors. For example, ButtonTheme has a ButtonTheme.bar() constructor.

To call the factory constructor instead of the default constructor, pass the constructor after the class name, separated with a colon.

Pug
generated Dart
button-theme:bar
| some content here
ButtonTheme.bar(
child: Text(
'some content here'
),
)

Passing children instead of child

There may be cases where flutter-view does not recognise your tag needs a children parameter instead of a child parameter. In that case use and array and assign it as children:

Pug
generated Dart
column
array(as='children')
row first!
row second!
Column(
children: [
Row(
children: [
Text('first!'),
]
),
Row(
children: [
Text('second!'),
]
),
]
)

Passing const widgets

For optimising performance, you may want to pass some widgets as constants. You can do this by adding a const parameter in a widget:

To see how this works, compare the Pug and generated Dart code in this example:

Pug
generated Dart
container
column(const)
row
text(:value='first row!')
return Container(
child: const Column(
children: [
Row(
children: [
Text('first row!'),
],
),
],
),
);

Passing parameters

To pass parameters besides child or children, you pass them as pug/html parameters.

String parameters

You can pass strings by using quotes. Both double and single quotes work.

For example:

Pug
generated Dart
banner(title='testing')
| Hello world!
return Banner(
title: 'testing',
child: Text('Hello world!'),
);

Expression parameters

To assign a Dart expression as the value of a parameter:

  • start the parameter name with :

  • wrap the expression in quotes. You may need to escape strings.

For example:

Pug
generated Dart
flat-button(:color='highlighted ? Colors.red : Colors.grey')
| Click me!
return FlatButton(
color: highlighted ? Colors.red : Colors.grey,
child: Text('Click me!'),
);

For title we want to pass a direct string, but for color we want to pass a value by reference, so we add : in front of the parameter.

Unnamed parameters

Some widgets take an unnamed parameter in their constructor. You can pass this using the reserved value parameter:

Pug
generated Dart
container
icon(:value='Icons.add')
text(value='Hello world')
return Container(
child: Column(
children: [
Icon(Icons.add),
Text('Hello world'),
]
),
);

Escaping parameter names

You may need to pass a parameter that has the name of a reserved keyword, such as value. You can bypass this problem by escaping your parameter with the ^ sign:

:^value='foo'

Passing complex parameter values

Sometimes what you want to pass is not just a single value, but a value that is constructed of many widgets. To solve this, you may take a child and use the as keyword to assign it as a parameter to the parent widget.

Pug
generated Dart
scaffold
app-bar(as='appBar')
container(as='title') Test App
center(as='body') Hello world!
return Scaffold(
appBar: AppBar(
title: Container(
child: Text('Test App'),
),
body: Center(
child: Text('Hello world!'),
),
),
);

The Scaffold class used above has no child or children parameters. Instead we add two children and assign them as parameters using the as property.

Passing functions as children

You can create functions that return children using the function shortcut.

Passing handlers

Passing a handler function or closure is no different than in Dart:

my-button(flutter-view :on-click[Function])
flat-button(:on-pressed='onClick') Click me!

A common case is a closure without any parameters. In that case you can use the @ sign to create a closure handler:

Pug
generated Dart
my-button(flutter-view)
flat-button(@on-pressed='print("Click!")') Click me!
MyButton() {
return FlatButton(
onPressed: () { print("Click!"); },
child: Text('Click me!'),
);
}

Passing Arrays

Sometimes you need to pass an array of specific items to a parameter. In that case you can use the array tag.

Pug
generated Dart
custom-scroll-view
array(as='slivers')
sliver1
sliver2
sliver3
CustomScrollView(
slivers: [
sliver1,
sliver2,
sliver3,
],
)

Automatic Containers

A nice Pug feature is that classes and ids are automatically converted into DIV tags. In flutter-view, they are automatically converted into Container widgets:

container hello world
#greeting hello world
.greeting hello world

All three are equivalent and convert to:

Container(
child: Text('hello world'),
)

The classes and ids you use are forgotten after the conversion to Dart code. However, you do get automatic commenting, which will make it easier to read the generated code.

The biggest benefit is however that you can use them to style your widgets.