이하 본문은 Flutter공식페이지 매뉴얼의 Row class를 번역하여 작성한 블로그입니다.
원문: https://api.flutter.dev/flutter/widgets/Row-class.html
Overview

Row 위젯은 위젯트리의 자녀 위젯들을 “하나의 행”안에 나누어 보여주는 수평식 배열구조를 제공합니다.
Row위젯의 자녀로 등록된 위젯 중 하나를 가지고 남은 공간을 꽉 채워서 보여주고 싶으시다면 Expanded 위젯을 사용해보세요.


해당 위젯이 빈 공간을 꽉 채우게 만들수 있습니다.
Row위젯은 한줄에 칸을 나누어 화면을 구성하는 만큼 스크롤이 되게 만들지 않습니다(일적으로 Row위젯의 자식들이 공간에 딱 맞게 구성이 안되고 자식이 너무 많거나 한 이유로 스크롤이 생길만큼 내려온다면 오류로 간주합니다). 만약 공간이 충분하지 않아서 스크롤을 해야하는 상황이라면 ListView를 사용해보세요.
자식 위젯들을 “하나의 열”에 수직으로 나열하고 싶으시면 Column을 사용해보세요.
만약 Row위젯 안에 자식이 단 하나만 존재할 경우에는 Align이나 Center위젯을 사용는 것을 권장드립니다.

아래 예제에서 Row위젯으로 공간을 가로로 3개로 나누어 처음 두 칸에는 텍스트를, 마지막 칸에는 이미지를 넣어보도록하겠습니다.
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(),
),
),
],
)
문제해결 (Troubleshooting)
행에 노란색과 검은색 경고줄무늬가 있는 경우
고정된 행으로 구성된 화면에서 행의 가로 크기보다 안에 내용이 더 넓은 경우 보여주어야하는 내용이 넘쳐버리는 상황이 생기는데 이를 “오버플로”가 되었다고 합니다. 이렇게 되면 남은 공간이 없게 되어서 내용을 보여줄수 없게 되는데 이때 넘친 가장자리에 노란색과 검은색 줄무늬로 경고상자를 그려 이를 알려줍니다. 행 바깥쪽에 공간이 있으면 경고문이 빨간색 글자로 경고상자 옆에 출력됩니다.
이해를 돕기 위해 아래 코드를 보면서 자세히 설명드리겠습니다.
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는 로고의 가로크기가 24픽셀로 보여지도록 만든 함수입니다 . 24픽셀정도는 써도 다음 위젯들을 위한 공간은 충분할 것입니다. 그 다음으로 두번째 칸의 자식을 위해 Row가 적절한 크기를 할당하게 됩니다.

그런데 이 시점에서 Row는 한정된 가로 공간을 고려하지 않고 단지 현재 칸에 들어간 문자열의 길이만 보고 너비를 결정합니다. 그렇게 되면 다음 칸에 보여줄 공간이 부족하게 되어 노랑/검정 경고상자와 빨간색 경고문자를 보여주면서 불평을 하는 것이지요.
이 문제를 해결하는 방법은 두번째 칸의 자식을 Expanded 위젯으로 감싸는 것입니다. 이렇게 하면 Expanded가 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는 첫번째 칸의 크기를 산정하고, 그 다음 세번째 칸의 크기를 산정한 다음 남은 공간을 모두 두번째 칸에 할당하게 되어 두번째 칸은 자신에게 정해진 공간이 한줄에 보여줄수 없음을 깨닫게 되고 여러줄에 나누어 보여줌으로써 좀더 융통성있는 화면 구성이 가능하게 됩니다.

만약 Row안의 자식들의 보여지는 순서를 이렇게 반대로 정렬하고 싶다면 Row의 textDirection속성을 이용하면 됩니다. 자식들을 출력하는 순서는 기본적으로 TextDirection.ltr, 즉 왼쪽에서 오른쪽으로 보여주는데, 이 속성을 아래 코드와 같이 TextDirection.rtl(right to left)로 변경하면 화면의 오른쪽에 첫번째 칸을 먼저 보여주게 되어 정렬이 반대로 됩니다.
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),
],
)
레이아웃 알고리즘 (Layout algorithm)
이번 섹션에서는 프레임웤이 어떻게 Row를 화면에 보여주는지 렌더링 알고리즘에 대해서 설명하도록 하겠습니다. 만약, 박스 레이아웃 모델이 궁금하시다면 BoxConstraints를 참고해주세요.
Row의 레이아웃을 처리하는 순서는 아래 6개의 단계가 있습니다:
- 각 자식 칸들을 flex속성에 “null”이나 “0”의 값을 주고 레이아웃을 요청합니다(단, 여기서 Expanded위젯을 사용하지 않았다는 전제하에서 말이죠). flex에 null이나 0을 주고 레이아웃을 요청하면 칸안쪽의 내용을 가늠해서 고정된 가로 크기를 임의로 할당 받게 됩니다. 이때 만약 flex속성에 특정 수치값을 주게 되면 해당 칸에 우선적으로 공간을 할당하고 남은 공간을 다른 칸에 나누어 부여합니다. 이때 unbounded 수평 제약, incoming 수직 제약을 사용하여 레이아웃을 요청하게 되는데, 만약 crossAxisAlignment이 CrossAxisAlignment.stretch로 설정되어있으면, incoming 최대 높이를 갖는 tight 수직 제약을 사용합니다.
- (마찬가지로, Expanded위젯을 사용한 자식칸이 없다는 전제하에) flex속성에 특정값이 주어진 칸에 우선적으로 가로크기를 할당하고, 남은 공간을 flex속성 값에 비례하여 칸을 나눕니다. 예를 들면 flex 속성에 2.0의 값을 가진 자식칸은 1.0의 값을 가진 칸의 두배크기의 가로공간을 할당 받게 됩니다.
- 첫번째 스텝에서와 마찬가지로 남은 자식칸들을 같은 수직 제약으로 레이아웃을 해줍니다. 다만 이때, unbounded 수직 제약을 사용하는 것이 아니라 스텝2에서 할당받은 공간을 기반으로 수평제약을 사용합니다. Flexible.fit 속성이 FlexFit.tight인 자식 칸은 타이트한 제약을 받게 됩니다(바로 여기에서 정해진 모든 공간을 가득채우도록 하는것이에요). 그리고 Flexible.fit속성이 FlexFit.loose로 설정된 칸은 널널한 제약을 받게 됩니다. (이때는 할당된 모든공간을 꽉 채우도록 강요받지 않아요)
- Row의 높이는 자식의 높이중 가장 높은 것으로 정해집니다. (이것이 바로 언제나 incoming 수직 제약을 충족시키는 조건이죠)
- Row의 넓이는 mainAxisSize속성에 의해 결정됩니다. mainAxisSize 속성이 MainAxisSize.max인 경우 Row의 너비는 incoming 제약 조건의 최대 너비입니다. mainAxisSize 속성이 MainAxisSize.min이면 Row의 너비는 자식 너비의 합계입니다(incoming 제약 조건에 따라 다름).
- mainAxisAlignment나 crossAxisAlignment에 따라 각 자식칸의 위치를 결정합니다. 예를 들어, mainAxisAlignment가 MainAxisAlignment.spaceBetween인 경우 자식칸에 할당되지 않은 남은 공간은 균등하게 나누어 자식 칸들 사이에 공백으로 채워집니다.
그 밖에도:
- Column, 같은 기능을 하되 수직으로 자식들을 나열함.
- Flex, 자식들을 수직으로 나열할지 수평으로 나열할지 결정되지 않은 경우에 사용.
- Expanded, Expanded로 감싸인 자식칸이 남은 모든 공간을 차지하도록 함.
- Flexible, 남은 공간을 함께 써야하는 자식칸들을 지정하는 데 사용되며 크기가 더 작아질수 있음 (나머지 공간 사용하지 않은채 남겨두기)
- Spacer, flex 값에 비례하여 공간을 차지하는 위젯입니다.
- 레이아웃 위젯 모음집
상속 계보 (Inheritance)
Object > DiagnosticableTree > Widget > RenderObjectWidget > MultiChildRenderObjectWidget > Flex > Row
생성자 (Constructors)
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>[]})Creates a horizontal array of children.const
속성 (Properties)
children → List<Widget> 바로 여기에 자식칸들을 배열로 나열합니다.final
,inherited
clipBehavior → Clip 이 옵션에 따라서 콘텐츠가 잘리거나 잘리지 않습니다.final
,inherited
crossAxisAlignment → CrossAxisAlignment 교차 축을 따라 자식칸들을 배치하는 방법을 기술합니다.final
,inherited
direction → Axis 주축으로 사용할 방향입니다.final
,inherited
hashCode → int 이 개체의 해시코드입니다.read-only
,inherited
key → Key? 하나의 위젯이 트리의 다른 위젯을 대체하는 방법을 제어합니다.final
,inherited
mainAxisAlignment → MainAxisAlignment 주축을 따라 하위 요소를 배치하는 방법입니다.final
,inherited
mainAxisSize → MainAxisSize 주축에서 얼마나 많은 공간을 차지해야할지 기술하는 부분입니다.final
,inherited
runtimeType → Type 객체의 런타임 유형을 나타냅니다.read-only
,inherited
textBaseline → TextBaseline? 기준선에 따라 항목을 정렬하는 경우 사용할 기준선입니다.final
,inherited
textDirection → TextDirection? 자식칸들을 가로로 배치하는 순서와 가로 방향의 시작과 끝을 해석하는 방법을 결정합니다.final
,inherited
verticalDirection → VerticalDirection 자식칸들을 세로로 배치하는 순서와 세로 방향의 시작과 끝을 해석하는 방법을 결정합니다.final
,inherited
클래스 내부함수 (Methods)
createElement() → MultiChildRenderObjectElement RenderObjectWidgets은 항상 RenderObjectElement의 하위클래스로 확장됩니다.inherited
createRenderObject(BuildContext context) → RenderFlex 이 RenderObjectWidget에 설명된 구성을 사용하여 이 RenderObjectWidget이 나타내는 RenderObject 클래스의 인스턴스를 생성합니다.inherited
debugDescribeChildren() → List<DiagnosticsNode> 이 노드의 하위 항목을 설명하는 DiagnosticsNode 객체 목록을 반환합니다.inherited
debugFillProperties(DiagnosticPropertiesBuilder properties) → void 노드와 관련된 추가 속성을 추가합니다.inherited
didUnmountRenderObject(covariant RenderObject renderObject) → void 이전에 이 위젯과 연관된 렌더링 객체가 트리에서 제거되었습니다. 지정된 RenderObject는 이 개체의 createRenderObject에서 반환된 것과 동일한 유형입니다.inherited
getEffectiveTextDirection(BuildContext context) → TextDirection? RenderFlex.textDirection에 전달할 값입니다.inherited
noSuchMethod(Invocation invocation) → dynamic 존재하지 않는 메서드나 속성에 액세스할 때 호출됩니다.inherited
toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) → DiagnosticsNode 디버깅 도구 및 DiagnosticsNode.toStringDeep에서 사용되는 개체의 디버그 표현을 반환합니다.inherited
toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) → String 이 개체의 문자열 표현입니다.inherited
toStringDeep({String prefixLineOne = ”, String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug}) → String 이 노드와 그 하위 항목의 문자열 표현을 반환합니다.inherited
toStringShallow({String joiner = ‘, ‘, DiagnosticLevel minLevel = DiagnosticLevel.debug}) → String 객체에 대한 한 줄의 자세한 설명을 반환합니다.inherited
toStringShort() → String 이 위젯에 대한 간단한 텍스트 설명입니다.inherited
updateRenderObject(BuildContext context, covariant RenderFlex renderObject) → void 이 RenderObjectWidget에 의해 설명된 구성을 지정된 RenderObject에 복사합니다. 이는 이 객체의 createRenderObject에서 반환된 것과 동일한 유형입니다.inherited
연산자 (Operators)
operator ==(Object other) → bool 같음 연산자입니다.inherited