Row

Row会将内部的子组件以水平方式排列

要让子组件横向填满可用空间,可以将其包裹在Expanded组件中。

Row组件本身不会滚动(通常认为当子组件超出可用空间时是一种错误)。如果有一些组件要渲染并且希望在空间不足时允许滚动,可以考虑使用ListView

如果只有一个子组件,可以考虑使用AlignCenter包裹。

默认情况下,crossAxisAlignmentCrossAxisAlignment.center,会在纵轴上将子组件居中对齐。如果多个子元素包含文本,并且它们的字体度量不同(比如由于TextStyle.fontSize或其他TextStyle属性不同,或因使用不同文字而采用不同字体),这可能会导致视觉上未对齐。此时可以考虑使用CrossAxisAlignment.baseline

举例

例子1

const Row(
  children: <Widget>[
    Expanded(
      child: Text('Deliver features faster', textAlign: TextAlign.center),
    ),
    Expanded(
      child: Text('Craft beautiful UIs', textAlign: TextAlign.center),
    ),
    Expanded(
      child: FittedBox(
        child: FlutterLogo(),
      ),
    ),
  ],
)

这个例子中将水平空间分为三块,前两个区域将文本内容居中放置在空间中,最后将一个FlutterLogo居中放置在最后一个空间中

常见问题

为什么Row会产生一个黄黑警告条?

如果Row中的非弹性内容(如没有被ExpandedFlexible包裹的子组件)总宽度超过了Row本身的宽度,就说明该Row发生了溢出。当Row溢出时,它已经没有剩余空间可供其ExpandedFlexible子组件分配。Row通过在溢出边缘绘制一个黄黑条纹警告框来报告这个情况。如果Row外部还有空间的话,溢出的量会用红色字体显示。

举个例子:

const Row(
  children: <Widget>[
    FlutterLogo(),
    Text("Flutter's hot reload helps you quickly and easily experiment, build UIs, add features, and fix bug faster. Experience sub-second reload times, without losing state, on emulators, simulators, and hardware for iOS and Android."),
    Icon(Icons.sentiment_very_satisfied),
  ],
)

首先Row询问它的第一个子元素FlutterLogo,想要按照它的大小进行布局。FlutterLogo要求了每边24像素,这为下一个子组件留下了大量的空间。接着Row询问下一个组件Text进行布局,但是Text并不知道自己需要多宽,就直接进行渲染,且没有进行换行。Row因为没有空间给其他子组件布局,于是展示了黄黑警告框。

解决方法就是将Text组件包裹在Expanded组件中,这样就会告诉Row组件,这个Text组件将会占据Row组件剩余空间。

const Row(
  children: <Widget>[
    FlutterLogo(),
    Expanded(
      child: Text("Flutter's hot reload helps you quickly and easily experiment, build UIs, add features, and fix bug faster. Experience sub-second reload times, without losing state, on emulators, simulators, and hardware for iOS and Android."),
    ),
    Icon(Icons.sentiment_very_satisfied),
  ],
)

现在Row会先对非弹性子组件进行布局,于是先对FlutterLogoIcon进行布局,之后再对被Expanded包裹的Text进行布局。将FlutterLogoIcon布局剩下的宽度告诉给TextText在剩下的宽度里面进行布局,发现宽度不够的时候自动进行换行。

textDirection属性控制着子组件的渲染方向。默认值为TextDirection.ltr,所以正常情况下第一个子组件出现在左边。想要更改方向可以将属性值设置为TextDirection.rtl

const Row(
  textDirection: TextDirection.rtl,
  children: <Widget>[
    FlutterLogo(),
    Expanded(
      child: Text("Flutter's hot reload helps you quickly and easily experiment, build UIs, add features, and fix bug faster. Experience sub-second reload times, without losing state, on emulators, simulators, and hardware for iOS and Android."),
    ),
    Icon(Icons.sentiment_very_satisfied),
  ],
)

布局算法

布局步骤:

  1. 先对非弹性子组件(如未被Expanded包裹的子组件)进行布局,使用无边界的宽度约束和Row的高度约束进行布局。如果crossAxisAlignmentCrossAxisAlignment.stretch则将高度约束变更为Row高度的紧约束(即强制高度与Row高度相同)
  2. 将剩余空间按照flex factor比例分配给弹性子组件
  3. 对弹性子组件进行布局,宽度约束不是无边界的宽度约束,而是第二步中分配完的剩余宽度约束。如果子组件的Flexible.fitFlexFit.tight则使用紧约束(强制填满剩余空间),若为FlexFit.loose则使用松约束(不强制填满剩余空间)
  4. Row的高度为子组件中的最大高度(始终满足传入的垂直约束)
  5. Row的宽度由mainAxisSize属性决定。若mainAxisSizeMainAxisSize.maxRow的宽度为传入约束的最大宽度,若为MainAxisSize.minRow的宽度为子组件宽度之和(受传入约束限制)
  6. 根据mainAxisAlignmentcrossAxisAlignment为每个子组件确定位置,比如mainAxisAlignmentMainAxisAlignment.spaceBetween,则未分配给子组件的水平空间会被均匀分配并放置在子组件之间

构造函数

Row.new({
  Key? key,
  MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
  MainAxisSize mainAxisSize = MainAxisSize.max,
  CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
  TextDirection? textDirection,
  VerticalDirection verticalDirection = VerticalDirection.down,
  TextBaseline? textBaseline,
  double spacing = 0.0,
  List<Widget> children = const <Widget>[]
})

参数

参数名参数类型说明
childrenList<Widget>子组件
clipBehaviorClip根据配置对组件进行裁切
crossAxisAlignmentCrossAxisAlignment交叉轴子组件排列方式
directionAxis组件排列方向,Row默认为水平方向不可调整
mainAxisAlignmentMainAxisAlignment主轴子组件排列方式
mainAxisSizeMainAxisSize主轴宽度是占满Row的宽度还是子组件宽度之和
spacingdouble相邻两个子组件的间距
textBaselineTextBaseline?设置子组件文字使用的基准线
textDirectionTextDirection?决定子组件在水平方向上的排列方向是从左往右还是从右往左
verticalDirectionVerticalDirection决定子组件在垂直方向上是从上往下还是从下往上