docs/codelabs/advanced_ui/3-flexbox.md
If you've never done any web programming, FlexBox may be unfamiliar. Even if you have worked with the web before, you may need a refresher.
FlexBox is a concept borrowed from CSS. It is an efficient way to layout children within a parent container even when their size is unknown or dynamic.
Valdi uses Yoga, Facebook's cross-platform flexbox layout engine, to implement flexbox layout. The behavior closely follows the CSS Flexible Box Layout specification with some extensions.
For more details: See Core Flexbox for an introduction and Style Attributes Reference for the complete API reference including Yoga-specific behaviors.
This is a selection of the most commonly used FlexBox attributes. You can find the complete list in the Style Attributes API Reference.
flexDirection: how the children should be arranged.
column: top to bottomcolumn-reverse: bottom to toprow: left to rightrow-reverse: right to leftjustifyContent: alignment along the main axis.
flex-start: at the startflex-end: at the endcenter: at the centerspace-between: evenly spacedspace-around: evenly space children. Children are centered with extra space on the left and rightspace-evenly: evenly distributed. Space between each pair of items; the start edge and the first item; the end edge and the last item; are all the same.alignItems: alignment along the cross axis.
stretch: strtech children to to match height of the containerflex-start: at the startflex-end: at the endcenter: at the centerbaseline: align children along a common baseline. An individual child can be set to be the reference baseline for the parent container.flexWrap: how to handle children that overflow the parent container.
no-wrap: no wrapping. Overflowing children will be hidden.wrap: overflow the children into the next row or column based on flexDirection.wrap-reverse: the same as wrap except the rows or columns are reversed.flexGrow: how space should be distributed among the children along the main axis. After laying out the children, the parent will distribute any remaining space according to the flexGrow values specified by the children.
0>= 0: A greater value than other children, will give that child more of the extra spaceflexShrink: how the parent should constrain the children if they overflow the parent container. The opposite of flexGrow. flexGrow and flexShrink can work together within the same parent.
0>= 0Let's display a list of dogs so we can see how these attributes impact layout.
Let's render our dogs in a separate function to make it eaiser to update them.
onRenderItems() {
for (let i = 0; i < 3; i++) {
const dog = 'https://placedog.net/50' + i;
<image src={dog} height={64} width={64} border='1 solid red' />;
}
}
The slightly different URLs will give us different dog images.
Now we can call this function from onRender.
onRender() {
<view padding='60 20' backgroundColor='green'>
<view backgroundColor='lightblue' width={250} height={250}>
{this.onRenderItems()}
</view>
</view>;
}
The outer <view> is the white background that you see. The inner <view> is the parent container that we're going to be playing with.
This layout is using all of the defaults. Checkout the UI, you should see a column of dogs.
flexDirectionflexDirection defaults to column, let's see what this looks like as a row.
<view backgroundColor='lightblue' width={250} height={250} flexDirection='row'>
{this.onRenderItems()}
</view>
Now we have a row of dogs.
If we specify row-reverse, we can get them in the opposite order and lined up on the right.
<view backgroundColor='lightblue' width={250} height={250} flexDirection='row-reverse'>
{this.onRenderItems()}
</view>
Going component by component is boring, let's do something more fun.
Duplicate the list of dogs so we have three of them and give the views a border so we can tell them apart.
<view backgroundColor='lightblue' width={250} height={250} flexDirection='row-reverse' border='1 solid green'>
{this.onRenderItems()}
</view>
<view backgroundColor='lightblue' width={250} height={250} flexDirection='row-reverse' border='1 solid green'>
{this.onRenderItems()}
</view>
<view backgroundColor='lightblue' width={250} height={250} flexDirection='row-reverse' border='1 solid green'>
{this.onRenderItems()}
</view>
Give 'em all different justifyContent values.
<view
backgroundColor='lightblue'
width={250}
height={250}
flexDirection='row-reverse'
border='1 solid green'
justifyContent='center'
>
{this.onRenderItems()}
</view>
<view
backgroundColor='lightblue'
width={250}
height={250}
flexDirection='row-reverse'
border='1 solid green'
justifyContent='space-between'
>
{this.onRenderItems()}
</view>
<view
backgroundColor='lightblue'
width={250}
height={250}
flexDirection='row-reverse'
border='1 solid green'
justifyContent='space-evenly'
>
{this.onRenderItems()}
</view>
Check out the subtle difference between space-between and space-evenly.
Let's do the same with alignItems to place dogs on the cross axis.
<view
backgroundColor='lightblue'
width={250}
height={250}
flexDirection='row-reverse'
border='1 solid green'
justifyContent='center'
alignItems='flex-end'
>
{this.onRenderItems()}
</view>
<view
backgroundColor='lightblue'
width={250}
height={250}
flexDirection='row-reverse'
border='1 solid green'
justifyContent='space-between'
alignItems='center'
>
{this.onRenderItems()}
</view>
<view
backgroundColor='lightblue'
width={250}
height={250}
flexDirection='row-reverse'
border='1 solid green'
justifyContent='space-evenly'
alignItems='flex-start'
>
{this.onRenderItems()}
</view>
Look at the UI, see that these values do something, go back and read the documentation above and make sure you understand why.
Setting child attributes can create some interesting results.
Let's update our dog rendering so that we can specify an attribute on only one child.
onRenderItems() {
for (let i = 0; i < 3; i++) {
const dog = 'https://placedog.net/50' + i;
if (i == 1) {
<image src={dog} height={64} width={64} border='1 solid blue' />;
} else {
<image src={dog} height={64} width={64} border='1 solid red' />;
}
}
}
Now we can do something different with the middle dog. Let's give him some grow.
<image src={dog} height={64} width={64} border='1 solid blue' flexGrow={2} />;
Check out the UI, the middle dog is now taking up extra space relative to his siblings.
Now let's change that to shrink.
<image src={dog} height={64} width={64} border='1 solid blue' flexShrink={2} />;
This looks the same as before. Why did nothing happen? flexShrink only has an impact when the children overflow the parent container and in our example, they all fit neatly in the parent.
Let's change that by making the dogs bigger.
if (i == 1) {
<image src={dog} height={100} width={100} border='1 solid blue' flexShrink={2} />;
} else {
<image src={dog} height={100} width={100} border='1 solid red' />;
}
Now you should be able to see the impact of flexShrink.
flexShrink and flexGrow can be used together in the same UI to create layouts that behave well in different sized parent containers.
Parking it here in case you need it.
import { StatefulComponent } from 'valdi_core/src/Component';
import { ScrollViewHandler } from 'coreui/src/components/scroll/ScrollViewHandler';
import { SectionList } from 'coreui/src/components/section/SectionList';
import { RenderFunctionBody, RenderFunctionHeader, SectionModel } from 'coreui/src/components/section/SectionModel';
import { SemanticColor } from 'coreui/src/styles/semanticColors';
/**
* Internal state of the component
*/
interface PlaygroundState {}
/**
* @Component
*/
export class Playground extends StatefulComponent<{}, PlaygroundState> {
onRender() {
<view padding='60 20' backgroundColor={SemanticColor.Background.SUBSCREEN}>
<view
backgroundColor='lightblue'
width={250}
height={250}
flexDirection='row-reverse'
border='1 solid green'
justifyContent='center'
alignItems='flex-end'
>
{this.onRenderItems()}
</view>
<view
backgroundColor='lightblue'
width={250}
height={250}
flexDirection='row-reverse'
border='1 solid green'
justifyContent='space-between'
alignItems='center'
>
{this.onRenderItems()}
</view>
<view
backgroundColor='lightblue'
width={250}
height={250}
flexDirection='row-reverse'
border='1 solid green'
justifyContent='space-evenly'
alignItems='flex-start'
>
{this.onRenderItems()}
</view>
</view>;
}
onRenderItems() {
for (let i = 0; i < 3; i++) {
const dog = 'https://placedog.net/50' + i;
if (i == 1) {
<image src={dog} height={100} width={100} border='1 solid blue' flexShrink={2} />;
} else {
<image src={dog} height={100} width={100} border='1 solid red' />;
}
}
}
}