이하 본문은 Flutter공식페이지 매뉴얼의 Column class를 번역하여 작성한 블로그입니다.
원문: https://api.flutter.dev/flutter/widgets/Column-class.html
Overview
Column 위젯은 위젯트리의 자녀 위젯들을 “하나의 열” 안에 나누어 보여주는 수직의 배열구조를 제공합니다.
특정 자녀로 주어진 공간을 가득 채우고자 할때는 그 자식 위젯을 Expanded위젯으로 감싸면 할당된 공간의 남은 영역을 해당 위젯이 점유하게 됩니다.
Column 위젯은 스크롤이 안생기게 만듭니다(일반적으로 Column 안의 내용물에 스크롤이 생겼다면 오류로 간주합니다). 만약에 자식위젯들을 화면에 보이는 것보다 많이 두어 스크롤 하도록 만들고 싶다면 ListView 위젯을 사용할 것을 추천드립니다.

const Column(
children: <Widget>[
Text('Deliver features faster'),
Text('Craft beautiful UIs'),
Expanded(
child: FittedBox(
child: FlutterLogo(),
),
),
],
)
위의 예제를 보면 각 행의 문자열과 로고가 가운데 정렬이 되어있습니다.

다음 예제에서 는 crossAxisAlignment 가 CrossAxisAlignment.start로 설정하여 자식칸 들을 왼쪽 정렬을 합니다. 그리고 mainAxisSize 속성은 MainAxisSize.min로 설정하여 칼럼의 세로 크기가 컨텐츠에 딱 맞게 줄어듭니다.
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('We move under cover and we move as one'),
const Text('Through the night, we have one shot to live another day'),
const Text('We cannot let a stray gunshot give us away'),
const Text('We will fight up close, seize the moment and stay in it'),
const Text('It’s either that or meet the business end of a bayonet'),
const Text('The code word is ‘Rochambeau,’ dig me?'),
Text('Rochambeau!', style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 2.0)),
],
)
문제해결 (Troubleshooting)
세로길이에 제한이 정해져 있지 않을때
Column위젯 안에 하나이상의 Expanded나 Flexible항목이 존재한다고 가정해봅시다. 그런데 이 Column위젯이 최대 높이를 제한하지 않은 또다른 Column이나 ListView위젯 안에 있다면 실행시 Exception에러를 보게 될거에요. 그 이유는 자식들이 flex속성에 0이 아닌 값을 가지지만 세로 크기가 정해지지 않았기 때문입니다.
위에서 기술한 Exception에러가 나는 문제점은 Flexible이나 Expanded가 다른 자식칸들을 배치한 후에 남은 공간을 Flexible이나 Expanded로 감싼 애들끼리 나눠 가져야하는데, 이때 세로길이가 정해져 있지 않으면 남은 공간이 무제한이 되기때문에 무한대의 공간을 나눌수가 없어서 에러가 발생하는 겁니다.
이 문제는 Column이 왜 세로 길이를 정하지 않았는지를 정의하면 해결이 가능합니다. 이제 무슨 말이냐면요;
보통 이 문제가 발생하는 이유는 Column이 다른 Column안에 들어가 있을때 안쪽의 Column을 Expanded나 Flexible로 감싸고 있지 않기때문에 발생하는데요. 기본적으로 Column은 Expanded나 Flexible로 감싸져 있지 않은, 즉 세로 길이가 정해져 있지 않은 자식칸을 레이아웃 할때, 세로 길이를 자유롭게 정할수 있도록 제약을 하지 않습니다. 그 말인 즉슨, 세로길이를 정해놓지 않으면 자식칸 안의 컨텐츠에 따라서 세로 길이를 알맞게 줄이라는 의미입니다. 그래서 세로 길이가 정해져있지 않은 Column안에 또 다른 Column을 넣고 자식칸을 Expanded로 감싸서 나머지 공간을 채우라고 하면 안쪽의 Column은 바깥쪽의 Column에서 세로길이에 대한 정보를 받은게 전혀 없으니까, 나머지 공간이 얼만데? 하고 고개를 갸우뚱하게 되는거죠. 그래서 결론 부터 말씀드리자면 Column 안쪽에 또다른 Column을 넣을때는 서브 Column이 Expanded를 써야하는 경우에는 너에게 할당된 공간은 얼마다 하고 알려주는거죠. 그걸 알려주는 방법이 바로 inner Column을 Expanded나 Flexible로 감싸주는 것입니다.
이 에러가 뜨는 또 다른 이유중 하나는 Column이 ListView나 세로스크롤이 되는 위젯 안에 들어가 있는 경우입니다. 이 경우에 세로길이가 무한대가 되어 에러가 뜨게됩니다(근데 세로 스크롤의 핵심은 세로길이를 무한대로 주어 계속 스크롤 할수 있게 하려는 것입니다). 그러니까 이런 경우에는 항상 확인을 하세요. 안쪽에 들어간 Column의 자식칸에서 Expanded나 Flexible로 감싼게 있는지 말이죠. 그러면 여러분은 고민하게 되실겁니다. 아 그러면 도대체 세로길이를 얼마로 정해줘야 하는거야? 라고 말이죠. 그럴때는 고민하지 말고 안쪽의 Column의 자식칸에서 사용한 Expanded나 Flexible위젯을 지워 버리세요. 그러면 Column이 컨텐츠의 길이에 맞춰서 세로 길이를 자동으로 할당할겁니다.
제약에 대해 좀더 자세하게 알고 싶으시면 BoxConstraints를 참조하세요.
노란색과 검은색 줄무늬의 배너가 있는 경우
고정된 Column으로 구성된 화면에서 Column의 세로길이보다 안에 컨텐츠가 더 긴 경우 보여주어야하는 내용이 넘쳐버리는 상황이 생기는데 이때 자리가 부족하면 컨텐츠가 짤려버립니다. 디버깅모드에서 노랑/검정 줄무늬 바는 컨텐츠가 정해진 범위를 넘어서 짤린다고 알려주는 경고입니다. 그리고 그 밑에 메세지는 얼마나 넘쳤는지 부족한 자리를 감지해서 알려줍니다.
이 문제를 해결하기 위해 보통 Column대신 ListView를 사용하여 세로공간이 부족한 경우 스크롤을 하도록 해서 모든 내용을 보여주도록 하는 것입니다.
Layout algorithm
이번 섹션에서는 프레임웤이 어떻게 Column을 화면에 보여주는지 렌더링 알고리즘에 대해서 설명하도록 하겠습니다. 만약, 박스 레이아웃 모델이 궁금하시다면 BoxConstraints를 참고해주세요.
Column의 레이아웃을 처리하는 순서는 아래 6개의 단계가 있습니다:
- 각 자식 칸들을 flex속성에 “null”이나 “0”의 값을 주고 레이아웃을 요청합니다(단, 여기서 Expanded위젯을 사용하지 않았다는 전제하에서 말이죠). flex에 null이나 0을 주고 레이아웃을 요청하면 칸안쪽의 내용을 가늠해서 고정된 세로 크기를 임의로 할당 받게 됩니다. 이때 만약 flex속성에 특정 수치값을 주게 되면 해당 칸에 우선적으로 공간을 할당하고 남은 공간을 다른 칸에 나누어 부여합니다. 이때 만약 crossAxisAlignment이 CrossAxisAlignment.stretch로 설정되어있으면, incoming 최대 넓이를 갖는 타이트한 수평 제약을 사용합니다.
- (마찬가지로, Expanded위젯을 사용한 자식칸이 없다는 전제하에) flex속성에 특정값이 주어진 칸에 우선적으로 세로길이를 할당하고, 남은 공간을 flex속성 값에 비례하여 칸을 나눕니다. 예를 들면 flex 속성에 2.0의 값을 가진 자식칸은 1.0의 값을 가진 칸의 두배크기의 세로 공간을 할당 받게 됩니다.
- 첫번째 스텝에서와 마찬가지로 남은 자식칸들을 같은 수직 제약으로 레이아웃을 해줍니다. 다만 이때, unbounded 수직 제약을 사용하는 것이 아니라 스텝2에서 할당받은 공간을 기반으로 수직제약을 사용합니다. Flexible.fit 속성이 FlexFit.tight인 자식 칸은 타이트한 제약을 받게 됩니다(바로 여기에서 정해진 모든 공간을 가득채우도록 하는것이에요). 그리고 Flexible.fit속성이 FlexFit.loose로 설정된 칸은 널널한 제약을 받게 됩니다. (이때는 할당된 모든공간을 꽉 채우도록 강요받지 않아요)
- Column의 넓이는 자식의 넓이중 가장 넓은 것으로 정해집니다. (이것이 바로 언제나 incoming 수평 제약을 충족시키는 조건이죠)
- Column의 길이는 mainAxisSize속성에 의해 결정됩니다. mainAxisSize 속성이 MainAxisSize.max인 경우 Column의 길이는 incoming 제약 조건의 최대 길이입니다. mainAxisSize 속성이 MainAxisSize.min이면 Column의 길이는 자식 길이의 합계입니다(incoming 제약 조건에 따라 다름).
- mainAxisAlignment나 crossAxisAlignment의 설정값에 따라 각 자식칸의 시작점을 결정합니다. 예를 들어, mainAxisAlignment가 MainAxisAlignment.spaceBetween인 경우 자식칸에 할당되지 않은 남은 공간은 균등하게 나누어 자식 칸들 사이에 공백으로 채워집니다.
그 밖에도:
- Row, 같은 기능을 하되 수평으로 자식들을 나열함
- Flex, 자식들을 수직으로 나열할지 수평으로 나열할지 결정되지 않은 경우에 사용.
- Expanded, Expanded로 감싸인 자식칸이 남은 모든 공간을 차지하도록 함.
- Flexible, 남은 공간을 함께 써야하는 자식칸들을 지정하는 데 사용되며 크기가 더 작아질수 있음 (나머지 공간 사용하지 않은채 남겨두기)
- SingleChildScrollView, Column을 스크롤이 가능한 컨테이너 안에 넣고 사용하는 방법
- Spacer, flex 값에 비례하여 공간을 차지하는 위젯입니다.
- 레이아웃 위젯 모음집
상속 계보 (Inheritance)
Object > DiagnosticableTree > Widget > RenderObjectWidget > MultiChildRenderObjectWidget > Flex > Column
생성자 (Constructors)
Column({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 vertical 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