Visly

TwitterGithubBlogDocs

Documentation

Stretch is available for Rust, Kotlin, Swift, and JavaScript. We will be adding more language bindings over time. For general help on using flexbox we suggest reading css-tricks.com/snippets/css/a-guide-to-flexbox and playing around with flexboxfroggy.com. Or you can always read the spec itself at www.w3.org/TR/css-flexbox-1

Installation

Let's start by create a new project with cargo new hello_stretch --bin. Before we can write any code we have to add Stretch as a dependency in Cargo.toml.

Cargo.toml
[dependencies]
stretch = "0.3.2"

We are ready to use Stretch! Open up hello_stretch/src/main.rs and replace its contents with the following. Once finished you should be able to run cargo run to see the results of this basic layout calculation.

main.rs
use stretch::{style::*, node::{Node, Stretch}, geometry::Size};
 
fn main() {
  let stretch = Stretch::new();
  
  let node = stretch.new_node(Style {
      size: Size { 
          width: Dimension::Points(100.0), 
          height: Dimension::Points(100.0),
      },
      ..Default::default()
  }, vec![]).unwrap();
 
  stretch.compute_layout(node, Size::undefined()).unwrap();
  dbg!(stretch.layout(node).unwrap());
}

Basics

Nodes are the core building blocks of a stretch layout tree. A node needs a Style which describes the flexbox properties for the node. By adding nodes as children to a parent node we create a tree which we can perform layout on. The result of a layout computation is a tree of Layout nodes. This tree matches the structure of the Node tree but contains the computed layout of each node.

main.rs
use stretch::{style::*, node::{Node, Stretch}, geometry::Size};
 
fn main() {
  let stretch = Stretch::new();
  
  let node = stretch.new_node(Style { ..Default::default() }, vec![
    stretch.new_node(Style {
      size: Size { 
          width: Dimension::Points(100.0), 
          height: Dimension::Points(100.0),
      },
      ..Default::default()
    }).unwrap()
  ]).unwrap();
  
  stretch.compute_layout(node, Size::undefined()).unwrap();
  let layout = stretch.layout(node).unwrap();
  
  layout.width; // 100.0
  layout.height; // 100.0
}

Nodes can be be mutated and stretch will automatically recompute only the subtrees which have changed. This is incredibly powerful if you have a large node tree, perhaps representing the UI of an app, and only need to change the height of a single element.

main.rs
use stretch::{style::*, node::{Node, Stretch}, geometry::Size};
 
fn main() {
  let stretch = Stretch::new();
  
  let mut node = stretch.new_node(Style { ..Default::default() }, vec![
    stretch.new_node(Style {
      size: Size { 
          width: Dimension::Points(100.0), 
          height: Dimension::Points(100.0),
      },
      ..Default::default()
    }).unwrap()
  ]).unwrap();
  
  stretch.compute_layout(node, Size::undefined()).unwrap();
  
  // Mutate node
  stretch.set_style(node, Style {
    size: Size { 
        width: Dimension::Points(100.0), 
        height: Dimension::Points(100.0),
    },
    ..Default::default()
  }).unwrap();
 
  // This call will return partially cached results
  stretch.compute_layout(node, Size::undefined()).unwrap();
}

Stretch

Create a new node with children (or an empty list). Children can be removed or added later on as well.

Stretch::new_node(style: Style, children: Vec<&Node>) -> Result<Node, Error>

Create a new leaf node. This is a node without children which knows how to measure its own intrinsic size. Examples of common leaf nodes are images and text.

Stretch::new_leaf(style: Style, measure: MeasureFunc) -> Result<Node, Error>

Set the measure function on a node. This function will be called on leaf nodes to resolve the instrinsic size of the node given a set of constraints.

Stretch::set_measure(&mut self, node: Node, measure: Option<MeasureFunc>) -> Result<(), Error>

Adds a child to an existing node.

Stretch::add_child(&mut self, node: Node, child: Node) -> Result<(), Error>

Replaces the children of an existing node.

Stretch::set_children(&mut self, node: Node, children: Vec<Node>) -> Result<(), Error>

Removes a previously added child from an existing node.

Stretch::remove_child(&mut self, node: Node, child: Node) -> Result<Node, Error>

Removes the child at the given index of an existing node.

Stretch::remove_child_at_index(&mut self, node: Node, index: usize) -> Result<Node, Error>

Replaces the child at the given index of an existing node with the given new child.

Stretch::replace_child_at_index(&mut self, node: Node, index: usize, child: Node) -> Result<Node, Error>

Returns the list of children owned by this node.

Stretch::children(&self, node: Node) -> Result<Vec<Node>, Error>

Returns the number of children associated with this node.

Stretch::child_count(&self, node: Node) -> Result<usize, Error>

Updates the style of an existing node.

Stretch::set_style(&mut self, node: Node, style: Style) -> Result<(), Error>

Returns the current style of a node.

Stretch::style(&self, node: Node) -> Result<&Style, Error>

Marks the node as dirty and propagates this up the node tree. This function is called internally for any mutating function. The only time you have to ensure to call it manually is if some application state has changed which will effect the intrinsic size of a leaf node. For example if the text of a text node has changed.

Stretch::mark_dirty(&mut self, node: Node) -> Result<(), Error>

Returns whether of not this node is marked as dirty and will need to be recalculated during the next layout pass.

Stretch::dirty(&self, node: Node) -> Result<bool, Error>

Computes the layout of a node tree given a size. The size passed in is used to compute percentage sizes on the root node. If you don't need to do this then it is fine to pass Size::undefined() however you will typically pass in the size of the viewport which will host this computed layout. To retries the resulting layout of a node in the tree pass the node to Stretch::layout().

Stretch::compute_layout(&mut self, node: Node, size: Size<Number>) -> Result<(), Error>

Returns the layout for the given node. Only call after Stretch::compute_layout().

Stretch::layout(&self, node: Node) -> Result<&Layout, Error>

Style

The Style struct contains all the properties associated with flexbox as well as some properties which we found useful outside of flexbox. For example position_type can be set to PositionType::Absolute which puts the node into a absolute layout context instead of a flexbox context. aspect_ratio is another property not part of the flexbox specification which when set ensures the node matches a certain aspect ratio.

struct Style {
    pub display: Display,
    pub position_type: PositionType,
    pub direction: Direction,
    pub flex_direction: FlexDirection,
    pub flex_wrap: FlexWrap,
    pub overflow: Overflow,
    pub align_items: AlignItems,
    pub align_self: AlignSelf,
    pub align_content: AlignContent,
    pub justify_content: JustifyContent,
    pub position: Rect<Dimension>,
    pub margin: Rect<Dimension>,
    pub padding: Rect<Dimension>,
    pub border: Rect<Dimension>,
    pub flex_grow: f32,
    pub flex_shrink: f32,
    pub flex_basis: Dimension,
    pub size: Size<Dimension>,
    pub min_size: Size<Dimension>,
    pub max_size: Size<Dimension>,
    pub aspect_ratio: Number,
}

AlignItems describes how to align children along the cross axis of their parent. AlignItems is very similar to JustifyContent but instead of applying to the main axis, AlignItems applies to the cross axis.

  • FlexStart aligns children of a container to the start of the container's cross axis.
  • FlexEnd aligns children of a container to the end of the container's cross axis.
  • Center aligns children of a container in the center of the container's cross axis.
  • Baseline aligns children of a container to the baseline of that container's cross axis.
  • Stretch stretches children of a container to match the height of the container's cross axis.

Optional, if left undefined Stretch is the default value.

enum AlignItems {
    FlexStart,
    FlexEnd,
    Center,
    Baseline,
    Stretch,
}

AlignSelf has the same options and effect as setting AlignItems on the containing node but instead of affecting the children within the node you can apply this property to a single child to change its alignment within its parent. AlignSelf overrides any option set on the parent with AlignItems.

  • Auto delegates to the parent's AlignItems property.
  • FlexStart aligns children of a container to the start of the container's cross axis.
  • FlexEnd aligns children of a container to the end of the container's cross axis.
  • Center aligns children of a container in the center of the container's cross axis.
  • Baseline aligns children of a container to the baseline of that container's cross axis.
  • Stretch stretches children of a container to match the height of the container's cross axis.

Optional, if left undefined Auto is the default value.

enum AlignSelf {
    Auto,
    FlexStart,
    FlexEnd,
    Center,
    Baseline,
    Stretch,
}

AlignContent defines the distribution of lines along the cross-axis. This only has effect when items are wrapped to multiple lines using FlexWrap.

  • FlexStart aligns wrapped lines to the start of the container's cross axis.
  • FlexEnd aligns wrapped lines to the end of the container's cross axis.
  • Center aligns wrapped lines in the center of the container's cross axis.
  • Stretch stretches wrapped lines to match the height of the container's cross axis.
  • SpaceBetween evenly spaces wrapped lines across the container's main axis, distributing remaining space between the lines.
  • SpaceAround evenly spaces wrapped lines across the container's main axis, distributing remaining space around the lines. Compared to space between using space around will result in space being distributed to the beginning of the first lines and end of the last line.

Optional, if left undefined Stretch is the default value.

enum AlignContent {
    FlexStart,
    FlexEnd,
    Center,
    Stretch,
    SpaceBetween,
    SpaceAround,
}

The Direction property specifies whether the component should be laid out in LTR or RTL writing mode.

Optional, Inherit by default (root node will implicitly default to LTR).

enum Direction {
    Inherit,
    LTR,
    RTL,
}

The Display property specifies whether the component should be laid out as Flex or not at all. If None is specified then the node will have a resulting size of zero. In the future this enum will be expanded to include more display options such as Grid.

Optional, Flex by default.

enum Display {
    Flex,
    None,
}

FlexDirection controls the direction in which children of a node are laid out. This is also referred to as the main axis. The main axis is the direction in which children are laid out. The cross axis the axis perpendicular to the main axis, or the axis which wrapping lines are laid out in.

  • Row aligns children from left to right. If wrapping is enabled then the next line will start under the first item on the left of the container.
  • Column aligns children from top to bottom. If wrapping is enabled then the next line will start to the left first item on the top of the container.
  • RowReverse aligns children from right to left. If wrapping is enabled then the next line will start under the first item on the right of the container.
  • ColumnReverse aligns children from bottom to top. If wrapping is enabled then the next line will start to the left first item on the bottom of the container.

Optional, if left undefined Row is the default value.

enum FlexDirection {
    Row,
    Column,
    RowReverse,
    ColumnReverse,
}

JustifyContent describes how to align children within the main axis of their container. For example, you can use this property to center a child horizontally within a container with FlexDirection set to FlexDirection::Row or vertically within a container with FlexDirection set to FlexDirection::Column.

  • FlexStart aligns children of a container to the start of the container's main axis.
  • FlexEnd aligns children of a container to the end of the container's main axis.
  • Center aligns children of a container in the center of the container's main axis.
  • SpaceBetween evenly spaces of children across the container's main axis, distributing remaining space between the children.
  • SpaceAround evenly spaces of children across the container's main axis, distributing remaining space around the children. Compared to space between using SpaceAround will result in space being distributed to the beginning of the first child and end of the last child.
  • SpaceEvenly evenly spaces of children across the container's main axis, distributing remaining space around the children so that visually there is equal space around every child including at the edges.

Optional, if left undefined FlexStart is the default value.

enum JustifyContent {
    FlexStart,
    FlexEnd,
    Center,
    SpaceBetween,
    SpaceAround,
    SpaceEvenly,
}

The Overflow property specifies how the node should handle children which overflow their parent's size. This property is currently not used but may be used in the future.

Optional, Visible by default.

enum Overflow {
    Visible,
    Hidden,
    Scroll,
}

The PositionType property specifies whether the component should be laid out relative to its sibling or absolutely positioned within its parent.

Optional, Relative by default.

enum PositionType {
    Relative,
    Absolute,
}

The FlexWrap property controls what happens when children overflow the size of the container along the main axis. By default children are forced into a single line (which can shrink components).

  • Wrap enables wrapping of children if they overflow the main axis of the container.
  • NoWrap disables wrapping of children, children will instead shrink to fit the main axis or overflow depending on their flex_shrink setting.
  • WrapReverse enables wrapping of children if they overflow the main axis of the container. Unlike Wrap, lines are laid out in reverse order.

Optional, if left undefined NoWrap is the default value.

enum FlexWrap {
    NoWrap,
    Wrap,
    WrapReverse,
}

When PositionType is set to PositionType::Relative the position property specifies the offset the node should have from its default position. In the case position is set to PositionType::Absolute this property indicates the distance this node's edge should be from the container's corresponding edge.

All position properties are optional and Dimension::Undefined by default.

position: Rect<Dimension>

Margin effects the spacing around the outside of the node it is applied to. A node with margin will offset itself from the bounds of its parent but also offset the location of any siblings. If margin is set to auto then the margin is equal to the remaining space in that axis divided between all other auto margin values in that axis.

As apposed to layout engines Stretch does not support setting left/right margin but instead uses start/end to ensure equal support for right-to-left as well as left-to-right locales. In left-to-right locales start corresponds to left and end to right, the opposite is true in right-to-left locales.

All margin properties are optional and Dimension::Undefined by default, resulting in no margin.

margin: Rect<Dimension>

Padding affects the size of the node it is applied to. Padding will not add to the total size of a node if it has an explicit size set, instead it will inset the content. For auto-sized nodes padding will increase the size of the node as well as offset the location of any children.

As apposed to layout engines Stretch does not support setting left/right padding but instead uses start/end to ensure equal support for right-to-left as well as left-to-right locales. In left-to-right locales start corresponds to left and end to right, the opposite is true in right-to-left locales.

All padding properties are optional and Dimension::Undefined by default, resulting in no padding.

padding: Rect<Dimension>

Border affects the size of the node it is applied to. Border will not add to the total size of a node if it has an explicit size set, instead it will inset the content. For auto-sized nodes border will increase the size of the node as well as offset the location of any children.

As apposed to layout engines Stretch does not support setting left/right border but instead uses start/end to ensure equal support for right-to-left as well as left-to-right locales. In left-to-right locales start corresponds to left and end to right, the opposite is true in right-to-left locales.

All border properties are optional and Dimension::Undefined by default, resulting in no border.

border: Rect<Dimension>

Flex grow describes how any space within a container should be distributed among its children along the main axis. After laying out its children, a container will distribute any remaining space according to the flex grow values specified by its children. Flex grow accepts any floating point value >= 0. A container will distribute any remaining space among its children weighted by the child’s flex grow value. If flex_grow is set to 0 the node will not grow.

Optional, if left undefined 0.0 is the default value.

flex_grow: f32

Flex shrink describes how to shrink children along the main axis in the case that the total size of the children overflow the size of the container on the main axis. flex shrink is very similar to flex grow and can be thought of in the same way if any overflowing size is considered to be negative remaining space. These two properties also work well together by allowing children to grow and shrink as needed. Flex shrink accepts any floating point value >= 0. A container will shrink its children weighted by the child’s flex shrink value. If flex_shrink is set to 0 the node will not shrink.

Optional, if left undefined 1.0 is the default value.

flex_shrink: f32

Flex basis is an axis-independent way of providing the default size of an item along the main axis. Setting the flex basis of a child is similar to setting the width of that child if its parent is a container with FlexDirection::Row or setting the height of a child if its parent is a container with FlexDirection::Column. The flex basis of an item is the default size of that item, the size of the item before any flex grow and flex shrink calculations are performed.

Optional, similar to width and height the default value is undefined which means that the flex basis is determined by the size of the node's content.

flex_basis: Dimension

The size property specifies the size of the node’s content area. Size can be specified in pixels, points, or a percentage of the parent size. If set to Dimension::Undefined or Dimension::Auto the size is calculated based on the node’s content. What is meant by content is based on the kind of node, for example the size of a text node is the size of the text being displayed while the size of a flexbox node is the total size of its children.

Optional, Size::undefined() by default.

size: Size<Dimension>

The maximum size of a node, this property takes priority over all other properties. If left undefined the node has no maximum size.

Optional, Size::undefined() by default.

min_size: Size<Dimension>

The minimum size of a node, this property takes priority over all other properties. If left undefined the node has no minimum size.

Optional, Size::undefined() by default.

max_size: Size<Dimension>

Configures the aspect ratio of a node, very useful for media content.

  • Accepts any floating point value > 0.
  • Defined as the ratio between the width and the height of a node e.g. if a node has an aspect ratio of 2 then its width is twice the size of its height.
  • Respects the min and max dimensions of an item.
  • Has higher priority than flex grow
  • If aspect ratio, width, and height are set then the cross axis dimension is overridden.

Optional, if left undefined no aspect ratio is enforced.

aspect_ratio: Number

Layout

Layout nodes are created from a layout calculation and contain the layout output of the corresponding node.

struct Layout {
    pub size: Size<f32>,
    pub location: Point<f32>,
}

Number

Number closely mimics Option<f32>. We needed to implement our own optional type to be able to implement certain traits not found on the built in optional.

enum Number {
    Defined(f32),
    Undefined,
}

Geometry

The geometry module in stretch defines some basic geometric data types used throughout the API as well as internally in the library. This includes Rect, Size, and Point. They are fairly self explanatory except for Rect perhaps. Insteaf of the typical left, top, right, bottom fields you may expect the Rect struct in stretch opts for using start and end instead of left and right. This enables stretch to force users of its API to be accessible for RTL users.

struct Rect<T> {
    pub start: T,
    pub end: T,
    pub top: T,
    pub bottom: T,
}
 
struct Size<T> {
    pub width: T,
    pub height: T,
}
 
struct Point<T> {
    pub x: T,
    pub y: T,
}