플러터를 공부한지 그리 오래되지는 않았으나, 깊이가 없는 상태에서 주먹구구식으로 코드를 짜다보니 화면을 구현할 때 RenderFlex Overflowed 에러를 마주한적이 굉장히 많습니다. 특히 화면이 복잡할수록 디바이스 크기별로 대응가능한 Responsible UI를 구현하는것이 더욱더 까다롭고, 그에 따라 플러터 위젯의 제약조건에 대해 어느정도 지식이 없으면 RenderFlex Overflowed를 해결하는데 어려움을 겪을 수 밖에 없다는 것을 깨달았습니다.
이러한 저의 무지함에 비롯된 빈번한 RenderFlex Overflowed 에러에 대한 이해도를 높이고, 좀 더 빠르게 이슈를 해결해보고자 하는 의지에 본 포스팅을 작성하게 되었습니다. 플러터의 제약조건이란 무엇인지 대략 감을 잡는것을 목표로 하여 작성했습니다.
플러터 제약조건에 대한 공식 문서를 확인하고 싶다면 다음 링크를 클릭해주세요.
Flutter - Understanding constraints
Constraints go down. Sizes go up. Parent sets position
플러터에서는 위 문장을 강조하면서, 이 규칙을 알지 못하면 플러터의 레이아웃을 이해할 수 없다고 명시하고 있습니다.
Constraints go down
→ 위젯은 자신의 제약 조건을 부모 위젯으로부터 받아옵니다. 제약조건에는 min/max width, min/max height 4가지가 있습니다.
Sizes go up
→ 부모 위젯은 자식 위젯들에게 제약조건을 알려줍니다. 그리고 각각의 자식 위젯에게 원하는 사이즈를 물어본 후 자식 위젯들을 차례차례 배치합니다.
Parent sets position
→ 마지막으로 자식 위젯들은 제약조건을 받아온 부모 위젯에게 자신의 사이즈를 알려줍니다.
플러터의 레이아웃 엔진은 one-pass 프로세스로 설계되었습니다. 이는 플러터가 위젯을 매우 효율적으로 배치하지만, 다음의 몇가지 제한사항이 있음을 의미합니다.
플러터에서 위젯들은 자신의 RenderBox에 그려집니다. RenderBox의 크기는 부모 위젯으로부터 받은 제약조건에 의해 결정됩니다.
https://api.flutter.dev/flutter/rendering/RenderBox-class.html
Container의 경우 크기가 고정되지 않으면 확장 가능한 최대 크기로 커지지만, 크기가 고정되면 고정된 크기만큼 확장됩니다.
Row 와 Column(flex box)도 주어진 제약조건에 따라 크기를 확장합니다. 공식 문서에서 이 부분에 Flex 섹션을 태그해놓아서 해당 부분에 대해 잠깐 짚고 넘어가봅니다.
https://docs.flutter.dev/ui/layout/constraints#flex
Row와 Column 같은 위젯들은 주어진 제약조건이 bounded인가 unbounded인가에 따라 다르게 동작합니다. 제약조건이 bounded일 때 자신의 주축 방향으로 가능한 한 크게 확장합니다. unbounded일 때는 자식 위젯들의 크기에 맞춰집니다. 이때 각 위젯의 flex 값은 0으로 설정되어야 합니다. 즉 flex box가 다른 flex box나 스크롤뷰에 있을 때 그의 자식 위젯으로 Expanded를 사용할 수 없습니다. 자식 위젯인 Expanded가 부모 위젯의 제약조건(unbounded)으로 인해 자신의 크기를 무제한으로 확장하려 하기 때문입니다(사용한다면 RenderFlex children have non-zero flex but incoming height constraints are unbounded 를 마주하게 될 겁니다).
플러터의 제약조건을 더욱 더 이해하기 위해 공식문서에 있는 3가지의 예제와 제가 직접 커스텀한 예제를 가져왔습니다. 예제에 나와있는 각 위젯들은 너비와 높이 모두 double.infinity
(너비와 높이가 double.infinity
로 설정된 경우 화면을 꽉 채웁니다) 값을 가진 ConstrainedBox
의 자식 위젯으로 배치됩니다.
Expanded(
child: ConstrainedBox(
constraints: BoxConstraints.tightFor(
width: double.infinity, height: double.infinity),
child: widget.examples[count - 1]),
),
Center(
child: Container(
width: double.infinity, height: double.infinity, color: red),
)
Center(
child: Container(color: red),
)
Center(
child: Container(
color: red,
child: Container(color: green, width: 30, height: 30),
),
)
Center(
child: Container(
width: 300,
height: 300,
color: red,
child: Container(color: green, width: 30, height: 30),
),
)
https://docs.flutter.dev/ui/layout/constraints
https://jinhan38.com/146
https://bsscco.github.io/posts/2019-03-13-flutter-dealing-with-box-constraints/
https://nayotutorial.tistory.com/63