When introducing Row and Column, an overflow error is reported if the child component is out of screen range, such as:

Row(
  children: <Widget>[
    Text("xxx"* 100)],);Copy the code

The running effect is as follows:

As you can see, the overflow section on the right is an error. This is because the Row is only one line by default and does not fold if it goes beyond the screen. We call the layout that folds automatically when it goes beyond the screen a streaming layout. Flutter supports streaming layout with Wrap and Flow. If Row is replaced with Wrap in the previous example, the overflow part will fold automatically.

Wrap

Here is the definition of Wrap:

Wrap({ ... Horizontal = Axis. Horizontal, this.alignment = WrapAlignment. Start, this.spacing = 0.0, This. RunAlignment = WrapAlignment. Start, enclosing runSpacing = 0.0, enclosing crossAxisAlignment = WrapCrossAlignment. Start, this.textDirection, this.verticalDirection = VerticalDirection.down, List<Widget> children = const <Widget>[], })Copy the code

We can see that many Wrap attributes have the same meaning in Row, Column, and Flex, such as direction, crossAxisAlignment, textDirection, verticalDirection, and children. Note, however, that the crossAxisAlignment attribute in Wrap is slightly different from the crossAxisAlignment attribute in Row, Column, and Flex:

  • Different value modes; Wrap the crossAxisAlignment values for WrapCrossAlignment. Start, and the Row and Column, as well as in the Flex crossAxisAlignment values as crossAxisAlignment. The center, WrapCrossAlignment is used for the former, while CrossAxisAlignment is used for the latter.

  • When the main axis is vertical, the alignment of sub-components is slightly different. As in Wrap crossAxisAlignment value of WrapCrossAlignment. Center, and the direction for the Axis, vertical and width of the screen width at the same time, this all child components in the Wrap are centered on the width of the largest component alignment, CrossAxisAlignment values in the Column for crossAxisAlignment center, and width for the screen width, then all subcomponents of them will be in the middle of the screen width center alignment and to maximize the component.

    Example code for Wrap:

    ConstrainedBox(
      constraints: BoxConstraints(minWidth: double.infinity),
      child: Container(
        color: Colors.blue,
        child: Wrap(
          direction: Axis.vertical,
          crossAxisAlignment: WrapCrossAlignment.center,
          children: <Widget>[
            Chip(
              avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("A"),),
              label: Text("Hamilton"),
            ),
            Chip(
              avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("B"),),
              label: Text("LafayetteLafayette"),
            ),
            Chip(
              avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("C"),),
              label: Text("Mulligan"),
            ),
            Chip(
              avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("D"),),
              label: Text("Laurens"(), [(), [(), [(Copy the code

    Operation effect:

    Sample code for Column:

    ConstrainedBox(
      constraints: BoxConstraints(minWidth: double.infinity),
      child: Container(
        color: Colors.blue,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Chip(
              avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("A"),),
              label: Text("Hamilton"),
            ),
            Chip(
              avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("B"),),
              label: Text("LafayetteLafayette"),
            ),
            Chip(
              avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("C"),),
              label: Text("Mulligan"),
            ),
            Chip(
              avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("D"),),
              label: Text("Laurens"(), [(), [(), [(Copy the code

    Operation effect:

Other attributes are the same, and I won’t go over them all here. You can refer to Row and Colum’s articles. Let’s take a look at a few properties that are unique to Wrap:

  • spacing: Spacing of sub-components in the spindle direction
  • runSpacing: Spacing of child components along the vertical axis
  • alignment: Alignment of subcomponents in the spindle direction
  • runAlignment: Alignment of child components along the vertical axis

The sample

Container(color: color. blue, child: Wrap(spacing: 20.0, runSpacing: 10.0, alignment: WrapAlignment. Center, children: <Widget>[ Chip( avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("A"),),
        label: Text("Hamilton"),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("B"),),
        label: Text("Lafayette"),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("C"),),
        label: Text("Mulligan"),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("D"),),
        label: Text("Laurens"),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("D"),),
        label: Text("Zane"(), [(), [(),Copy the code

The running effect is as follows:

We can see that the width and height of the Wrap are adaptive, depending on the subcomponents it contains. If we want to set the width and height of the Wrap, we can force changes to the width and height limits by ConstrainedBox and SizedBox, as in:

ConstrainedBox(constraints: BoxConstraints(minWidth: double. Infinity, minHeight: 600.0), child: Container(color: Colors.blue, child: Wrap( alignment: WrapAlignment.center, runAlignment: WrapAlignment.center, children: <Widget>[ Chip( avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("A"),),
          label: Text("Hamilton"),
        ),
        Chip(
          avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("B"),),
          label: Text("LafayetteLafayette"),
        ),
        Chip(
          avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("C"),),
          label: Text("Mulligan"),
        ),
        Chip(
          avatar: CircleAvatar(backgroundColor: Colors.blue, child: Text("D"),),
          label: Text("Laurens"(), [(), [(), [(Copy the code

Flow

Flow is rarely used because it is too complex and requires the implementation of the position conversion of the child components, so in many scenarios the first consideration is whether Wrap meets the requirements. Flow is mainly used in scenarios where a custom layout strategy is required or where performance is required (such as in animation).

FlowThe advantages of

  • Good performance:FlowIs a very efficient control to adjust the size and position of child components,FlowUse the transformation matrix to optimize when adjusting the position of the child components: inFlowAfter positioning, if the size or position of the child component changes, inFlowDelegateIn thepaintChildren()Method callcontext.paintChildI’m going to redraw, andcontext.paintChildThe transformation matrix was used in the redraw without actually adjusting the component positions.
  • Flexibility: Because we need to implement it ourselvesFlowDelegateIn thepaintChildren()Method, so you need to calculate the position of each component, so you can customize the layout strategy.

FlowThe disadvantages of the

  • Use complexity (code implementation is still a bit of a foundation).
  • Child component sizing cannot be customized and must be done by specifying the parent container size orTestFlowDelegatethegetSizeReturn fixed size.

The sample

Custom streaming layout:

Flow(delegate: TestFlowDelegate(margin: edgeinset.all (10.0)), children: <Widget>[new Container(width: Color: color.red,), new Container(width: 80.0, height:80.0, color: color.red,) Color.green,), new Container(width: 80.0, height:80.0, color: color.blue,), new Container(width: 80.0, height:80.0, color: color.blue,), new Container(width: Color: color.yellow,), new Container(width: 80.0, height:80.0, color: color.yellow,) Colors. Brown,), new Container(width: 80.0, height:80.0, color: color.purple,),],)Copy the code

Implement TestFlowDelegate:

class TestFlowDelegate extends FlowDelegate { EdgeInsets margin = EdgeInsets.zero; TestFlowDelegate({this.margin}); @override void paintChildren(FlowPaintingContext context) { var x = margin.left; var y = margin.top; // Calculates the position of each child widgetfor (int i = 0; i < context.childCount; i++) {
      var w = context.getChildSize(i).width + x + margin.right;
      if{< context. The size. The width (w). The context paintChild (I, transform: new Matrix4 translationValues (x, y, 0.0)); x = w + margin.left; }else{ x = margin.left; y += context.getChildSize(i).height + margin.top + margin.bottom; / / draw the child widgets (optimization) context. PaintChild (I, transform: new Matrix4 translationValues (x, y, 0.0)); x += context.getChildSize(i).width + margin.left + margin.right; }} @override getSize(BoxConstraints constraints){// Specify the size of the FlowreturnThe Size (double. Infinity, 200.0); } @override bool shouldRepaint(FlowDelegate oldDelegate) {return oldDelegate != this;
  }
}
Copy the code

Operation effect:

As you can see, our main task is to implement the paintChildren() method, whose main purpose is to determine the location of each child component. Since Flow does not adapt to the size of its child components, we specify the size of Flow by returning a fixed size in the getSize() return.