Flutter的布局其实比web更简单直接,万物皆可Column与Row,这两个Flex类型组件将屏幕方方正正划分,其中Column组件让人又爱又恨,首先它让垂直方向的高度不再像web中那么难以控制,如果你在根组件中直接使用Column做最外层容器,它的默认高度就是屏幕高度,子组件又有Expanded能划分剩余空间,是不是很舒服?web中想对高度精确控制还要html,body都来个height:100%,每层组件自己定高,要划分高度不得不计算百分比。本质上因为web认为:文本水平方向排布,而元素从上到下堆砌,最终高度是所有元素换行后自然堆叠形成,人为控制垂直方向的高度不是优先选项。而Flutter加强了布局的规范,认为水平和垂直方向的布局都是同等重要的。

Column与Row都继承Flex组件,Flex布局我们早在CSS中打过交道,Flutter的Flex和CSS大同小异,本质上一样,但在一些附加能力上有差别,CSS的Flex直接百度阮一峰老师的Flex布局介绍就ok了,这里主要讲Flutter在这块的异同。

直接用Column做代表,源码中有如下解释,英文且比较长,早就看过就没必要听我归纳了,能直接阅读理解的也建议阅读源码,想轻松一点就听我在后面掰扯:

/// 1. Layout each child a null or zero flex factor (e.g., those that are not
///    [Expanded]) with unbounded vertical constraints and the incoming
///    horizontal constraints. If the [crossAxisAlignment] is
///    [CrossAxisAlignment.stretch], instead use tight horizontal constraints
///    that match the incoming max width.
/// 2. Divide the remaining vertical space among the children with non-zero
///    flex factors (e.g., those that are [Expanded]) according to their flex
///    factor. For example, a child with a flex factor of 2.0 will receive twice
///    the amount of vertical space as a child with a flex factor of 1.0.
/// 3. Layout each of the remaining children with the same horizontal
///    constraints as in step 1, but instead of using unbounded vertical
///    constraints, use vertical constraints based on the amount of space
///    allocated in step 2. Children with [Flexible.fit] properties that are
///    [FlexFit.tight] are given tight constraints (i.e., forced to fill the
///    allocated space), and children with [Flexible.fit] properties that are
///    [FlexFit.loose] are given loose constraints (i.e., not forced to fill the
///    allocated space).
/// 4. The width of the [Column] is the maximum width of the children (which
///    will always satisfy the incoming horizontal constraints).
/// 5. The height of the [Column] is determined by the [mainAxisSize] property.
///    If the [mainAxisSize] property is [MainAxisSize.max], then the height of
///    the [Column] is the max height of the incoming constraints. If the
///    [mainAxisSize] property is [MainAxisSize.min], then the height of the
///    [Column] is the sum of heights of the children (subject to the incoming
///    constraints).
/// 6. Determine the position for each child according to the
///    [mainAxisAlignment] and the [crossAxisAlignment]. For example, if the
///    [mainAxisAlignment] is [MainAxisAlignment.spaceBetween], any vertical
///    space that has not been allocated to children is divided evenly and
///    placed between the children.

首先这6步用中文总结:

1.排布那些flex属性为0或者null的元素。

2.flex不为0和null的元素按照flex值划分剩余空间,flex值是2.0的元素分到的空间是flex为1.0元素的两倍,注意这里只是分配空间,这些元素最终多大还是要看下一步。

3.依然处理flex不为0和null的元素,这时候fit属性生效,fit值为FlexFit.tight强制让该元素占满分到的空间;fit值为FlexFit.loose不要求占满分到的空间,什么意思?比如:元素本身高200,在第二步分到了300空间,fit值为FlexFit.loose,则最终元素自身显示的高度就是200,有100空间剩余。

4.确定Column的宽度,是子元素的最大宽度。

5.确定Column的高度,如果Column的mainAxisSize属性为MainAxisSize.max(默认值),最终高度是限制高度的最大值——如果有ConstrainedBox限制,就是BoxConstraints的maxHeight;如果没有ConstrainedBox,占用父元素中尽可能大的空间,这里需要注意在可滚动组件中使用Column的典型问题,还有一种特殊情况:Column里面再嵌套Column,那么只有对最外面的Column会占用尽可能大的空间,里面Column所占用的空间为实际的固有大小(Row也一样)。

6.上一步确定了Column的高度,现在根据mainAxisAlignment和crossAxisAlignment确定每个元素的位置,比如mainAxisAlignment为MainAxisAlignment.spaceAround,那么剩余的未被子元素占用的空间会在元素间均匀分布。举个例子,第3步例子中多余的100空间由于MainAxisAlignment.spaceAround会被均匀分配到各个子元素之间。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐