This paper mainly introduces the row and column control in flutter layout, introduces its layout behavior and usage scene in detail, and analyzes the source code.
1. Row
A widget that displays it children in a horizontal array.
1.1 Introduction
A multi-child node control, which is very common in flutter, arranges children into one line. It is estimated that the flex layout is borrowed from the Web, so many properties and performance are similar. Note, however, that it does not have a scrolling property, and if one line is exceeded, an overflow prompt appears under Debug.
1.2 Layout behavior
The layout of row has six steps, which are shown from flex (the parent class of row and column):
- First, according to the unrestricted spindle (main axis) constraints, the flex is null or 0 of the child layout, and then follow the intersection axis (cross axis) constraints, the child is adjusted;
- The remaining space in the spindle direction is divided into equal portions according to the non-null flex value;
- For the above steps the flex value is not empty child, in the direction of the cross-axis adjustment, in the spindle direction using the maximum constraints, so that it fills the space of step 2;
- The Flex cross axis is derived from the maximum cross axis of the child node;
- The value of the spindle flex is determined by the Mainaxissize property, where mainaxissize can take Max, Min, and specific value values;
- The location of each child is determined by mainaxisalignment and crossaxisalignment.
The layout behavior of row has so many steps on the surface that it is simple enough to refer to the flex layout in the Web, including the concepts of spindles, cross axes, and so on.
1.3 Inheritance Relations
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > MultiChildRenderObjectWidget > Flex > Row
Both row and column are subclasses of Flex, and their implementation is done by flex only, with different parameters.
1.4 Sample Code
Row( children: <Widget>[ Expanded( child: Container( color: Colors.red, padding: EdgeInsets.all(5.0), ), flex: 1, ), Expanded( child: Container( color: Colors.yellow, padding: EdgeInsets.all(5.0), ), flex: 2, ), Expanded( child: Container( color: Colors.blue, padding: EdgeInsets.all(5.0), ), flex: 1, ), ],)
A very simple example, using the expanded control, divides the width of a line into four equal parts, the first to third child occupies 1/4 of the area, the second child takes up 1/2 of the area, and is controlled by the Flex attribute.
1.5 Source Code parsing
The constructor functions are as follows:
Row({ Key key, MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, MainAxisSize mainAxisSize = MainAxisSize.max, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, TextDirection textDirection, VerticalDirection verticalDirection = VerticalDirection.down, TextBaseline textBaseline, List<Widget> children = const <Widget>[],})
1.5.1 Attribute parsing
mainaxisalignment: Alignment on the spindle direction, will work for the child's position, default is start.
Where Mainaxisalignment enumeration values:
- Center: Place the Children in the centre of the spindle;
- End: Place the children at the end of the spindle;
- Spacearound: The blank area of the spindle direction is divided evenly, so that the white space between the children is equal, but the white space of the child is 1/2;
- Spacebetween: Divide the blank area of the spindle direction, make the white space between the children equal, the end child is close to the head and tail, no gap;
- Spaceevenly: Divide the blank area of the spindle direction, make the white space between the children equal, including the child;
- Start: Place the children at the starting point of the spindle;
The difference between Spacearound, Spacebetween and Spaceevenly is the way to treat the child. The distance from the end of the distance is 1/2, 0, 1 of the blank area, respectively.
mainaxissize: A value that occupies space in the direction of the spindle, which is max by default.
There are two kinds of values for mainaxissize:
- Max: Maximizes available space in the spindle direction based on the layout constraints passed in;
- Min: In contrast to Max, it is the space available to minimize the spindle direction;
crossaxisalignment: Alignment of children in the direction of intersection, slightly different from mainaxisalignment.
There are several Crossaxisalignment enumeration values:
- Baseline: In the direction of the cross axis, so that the children baseline alignment;
- The Center:children is centered on the cross axis;
- The End:children is displayed at the end of the cross axis;
- The Start:children is displayed at the beginning of the crossing axis;
- Stretch: Let children fill the direction of the cross axis;
textdirection: Compatible settings for the Arabic language system, generally no need to process.
verticaldirection: Defines the children placement order, which is down by default.
There are two types of Verticaldirection enumeration values:
- Down: Layout from top to bottom;
- Up: layout from bottom to top.
The top corresponds to row and column, the left and the top, the bottom, the right and the bottom.
textbaseline: There are two ways to use the Textbaseline, as described earlier.
1.5.2 Source
The source code for row and column is a constructor, and the implementation is all in their parent class flex.
About the Flex constructor
Flex({ Key key, @required this.direction, this.mainAxisAlignment = MainAxisAlignment.start, this.mainAxisSize = MainAxisSize.max, this.crossAxisAlignment = CrossAxisAlignment.center, this.textDirection, this.verticalDirection = VerticalDirection.down, this.textBaseline, List<Widget> children = const <Widget>[],})
As you can see, the Flex constructor has one more parameter than row and column. The difference between row and column is exactly the same as this direction parameter. When it is axis.horizontal, it is row, and when it is axis.vertical, it is column.
Let's take a look at the flex layout function, because the layout functions are much more, so the segments are explained:
while (child = null) {final Flexparentdata childparentdata = Child.parentdata; totalchildren++; Final int flex = _getflex (child); if (Flex > 0) {totalflex + = Childparentdata.flex; Lastflexchild = child; } else {boxconstraints innerconstraints; if (crossaxisalignment = = crossaxisalignment.stretch) {switch (_direction) {case axis.horizontal:i nnerconstraints = new Boxconstraints (MinHeight:constraints.maxHeight, Maxh Eight:constraints.maxHeight); Break Case Axis.vertical:innerConstraints = new Boxconstraints (MinWidth:constraints.maxWidth, MaxWidth:constraints.maxWidth); Break }} else {switch (_direction) {case Axis.horizontal:innerConstraints = new Boxconstraints (maxhe Ight:constraints.maxHeight); Break Case Axis.vertical:innerConstraints = new BoxconsTraints (MaxWidth:constraints.maxWidth); Break }} child.layout (Innerconstraints, parentusessize:true); Allocatedsize + = _getmainsize (child); Crosssize = Math.max (crosssize, _getcrosssize (child)); } child = Childparentdata.nextsibling;}
The above code, I have some of the middle of the assert and error messages such as the code removed, do not affect the actual understanding.
At the beginning of the layout, the child is traversed first, and the traversal has a function of two points:
- For child with flex values, calculate Flex's and find the last child that contains flex values. Find this child, because the spindle alignment, it may be the position of the adjustment, need to find out;
- For a child that does not contain flex, the child is adjusted according to the cross-axis direction setting.
Final double freeSpace = Math.max (0.0, (Canflex maxmainsize:0.0)-allocatedsize); if (Totalflex > 0 | | crossaxisali Gnment = = crossaxisalignment.baseline) {Final double Spaceperflex = Canflex && totalflex > 0? (Freespace/totalflex): Double.NaN; Child = FirstChild; while (child! = null) {final int flex = _getflex (child); if (Flex > 0) {final double maxchildextent = Canflex? (Child = = Lastflexchild?) (freespace-allocatedflexspace): Spaceperflex * Flex): double.infinity; Double minchildextent; Switch (_getfit) {case FlexFit.tight:assert (Maxchildextent < double.infinity); Minchildextent = maxchildextent; Break Case FlexFit.loose:minChildExtent = 0.0; Break } boxconstraints innerconstraints; if (crossaxisalignment = = crossaxisalignment.stretch) {switch (_direction) {case Axis.horizontal: innerconstraints = new BoxconstrAints (Minwidth:minchildextent, Maxwidth:maxchildextent, MinHeight:constraints.maxHeight, Maxhe Ight:constraints.maxHeight); Break Case Axis.vertical:innerConstraints = new Boxconstraints (MinWidth:constraints.maxWidth, MaxWidth:constraints.maxWidth, Minheight:mi Nchildextent, maxheight:maxchildextent); Break }} else {switch (_direction) {case Axis.horizontal:innerConstraints = new Boxconstrain TS (minwidth:minchildextent, Maxwidth:maxchildextent, MaxHeight:constraints.maxHeight); Break CaSe Axis.vertical:innerConstraints = new boxconstraints (MaxWidth:constraints.maxWidth, Minheight:minchildextent, Maxheight:maxchilde Xtent); Break }} child.layout (Innerconstraints, parentusessize:true); Final double childsize = _getmainsize (child); Allocatedsize + = Childsize; Allocatedflexspace + = maxchildextent; Crosssize = Math.max (crosssize, _getcrosssize (child)); } if (crossaxisalignment = = crossaxisalignment.baseline) {Final double distance = Child.getdistancetobaseline (Tex Tbaseline, onlyreal:true); if (distance! = null) Maxbaselinedistance = Math.max (maxbaselinedistance, distance); } final Flexparentdata childparentdata = Child.parentdata; Child = childparentdata.nextsibling; }}
The above code snippet also has two things to do:
- Allocate the remaining space for a child that contains flex
For each flex space, it is calculated as follows:
final double freeSpace = math.max(0.0, (canFlex ? maxMainSize : 0.0) - allocatedSize);final double spacePerFlex = canFlex && totalFlex > 0 ? (freeSpace / totalFlex) : double.nan;
Where Allocatedsize is not included in the space occupied by flex. When the space occupied by each flex is calculated, the child containing flex is adjusted according to the setting of the cross axis.
- Calculate the baseline value
If the intersection axis is aligned to baseline, the maximum baseline value is computed as the overall baseline value.
switch (_mainAxisAlignment) { case MainAxisAlignment.start: leadingSpace = 0.0; betweenSpace = 0.0; break; case MainAxisAlignment.end: leadingSpace = remainingSpace; betweenSpace = 0.0; break; case MainAxisAlignment.center: leadingSpace = remainingSpace / 2.0; betweenSpace = 0.0; break; case MainAxisAlignment.spaceBetween: leadingSpace = 0.0; betweenSpace = totalChildren > 1 ? remainingSpace / (totalChildren - 1) : 0.0; break; case MainAxisAlignment.spaceAround: betweenSpace = totalChildren > 0 ? remainingSpace / totalChildren : 0.0; leadingSpace = betweenSpace / 2.0; break; case MainAxisAlignment.spaceEvenly: betweenSpace = totalChildren > 0 ? remainingSpace / (totalChildren + 1) : 0.0; leadingSpace = betweenSpace; break;}
Then, the child in the spindle direction in accordance with the alignment of the setting, the position adjustment. The above code is the process of calculating the value of the blank area before and after, you can see the difference between Spacebetween, Spacearound and Spaceevenly.
Double childmainposition = Flipmainaxis? Actualsize-leadingspace:leadingspace;child = firstchild;while (child = null) {final Flexparentdata Childparentdata = Child.parentdata; Double childcrossposition; Switch (_crossaxisalignment) {case CrossAxisAlignment.start:case CrossAxisAlignment.end:childCrossPosition = _startistopleft (Flipaxis (direction), textdirection, verticaldirection) = = (_crossaxisalignment = = Crossaxisalignment.start)? 0.0:crosssize-_getcrosssize (child); Break Case CrossAxisAlignment.center:childCrossPosition = crosssize/2.0-_getcrosssize (child)/2.0; Break Case CrossAxisAlignment.stretch:childCrossPosition = 0.0; Break Case CrossAxisAlignment.baseline:childCrossPosition = 0.0; if (_direction = = axis.horizontal) {assert (Textbaseline! = null); Final double distance = Child.getdistancetobaseline (textbaseline, onlyreal:true); if (distance! = null) childcrossposition = maxbaselinedistance-distance; } break; } if (Flipmainaxis) childmainposition-= _getmainsize (child); Switch (_direction) {Case Axis.horizontal:childParentData.offset = new offset (childmainposition, childcrossposit ION); Break Case Axis.vertical:childParentData.offset = new offset (childcrossposition, childmainposition); Break } if (Flipmainaxis) {childmainposition-= Betweenspace; } else {childmainposition + = _getmainsize (child) + betweenspace; } child = Childparentdata.nextsibling;}
Finally, based on the alignment settings of the intersection axis, the child is adjusted to position, and the layout ends.
We can go through the whole process:
- Calculate the sum of Flex and find the last child with Flex set;
- For a child that does not contain flex, the alignment is adjusted according to the intersection axis alignment, and the size of the area occupied by the spindle direction is calculated.
- Calculates the space occupied by each flex and adjusts the child containing flex according to the cross-axis alignment;
- If the intersection axis is set to baseline, the overall baseline value is calculated;
- Adjust the child according to the spindle alignment;
- Finally, according to the intersection axis alignment, all child positions are adjusted to complete the layout.
1.6 Usage Scenarios
Both row and column are very common layout controls. In general, for example, you can use a control when it is displayed in one row or column. It's not that you can use either row or column to layout, or stack to see the specific scene selection.
2. Column
When explaining the row, in fact, according to some of Flex's layout behavior, including source analysis, are also used in flex analysis. Both row and column are subclasses of Flex, except that the direction parameter is different. column is the same row, so there is no further explanation here.
When it comes to flex, it's also said to be referring to the flex layout of the Web, and if you have a relevant development experience, it's easier to understand how it's used and how it works.
3. Something
The author has built a flutter study related projects, GitHub address, which contains the author of the Flutter study related to some of the articles, will be updated regularly, will also upload some learning demo, welcome attention.
4. Reference
- Row class
- Column class
- mainaxisalignment enum
- crossaxisalignment enum
- Mainaxissize enum
- verticaldirection enum
Flutter layout (vii)-ROW, column detailed