개요
Flutter에는 부모위젯으로부터 제약을 받아 자신의 크기를 결정하는 위젯들이 존재합니다.
만약 부모의 크기가 정해지지 않다면 자신의 크기를 정하지 못해 오류가 발생합니다. 이에 대해서 알아봅시다.
Column - Column - (Expanded, Flexible)
Expanded와 Flexible은 부모의 사이즈를 채워주는 위젯입니다.
body: Column(
children: [
Container(
height: 50,
color: Colors.orange,
child: ...,
),
Expanded( // Flexible 로 마찬가지
child: Container(
color: Colors.blue,
child: ...,
),
),
Container(
height: 50,
color: Colors.orange,
child: ...,
),
],
),
Column 안에 Expanded를 넣으면 깔끔하게 채워줍니다. 이게 가능한 이유는
- Column / Row 는 부모로부터 제약조건을 받아야한다. 없다면 하위 위젯에 크기에 따라서 정해진다.
- Scaffold 의 body사이즈는 MediaQuery의 크기(화면 크기)에서 AppBar, BottomNavigationBar등이 차지하는 공간을 제외한 나머지 영역의 크기로 정해져있어 크기 제약조건이 존재한다.
- Column의 부모위젯에 제약조건이 존재한다.
따라서 Expanded 위젯을 사용하더라도 오류가 발생하지 않습니다.
그런데 만약에 Column - Column 안에 Expanded가 들어간다면
body: Column(
children: [
Container(
height: 50,
color: Colors.orange,
child: ...,
),
Column(
children: [
Expanded(
child: Container(
color: Colors.blue,
child: ...,
),
),
],
),
Container(
height: 50,
color: Colors.orange,
child: ...,
),
],
),
Column 안에 Column 에 Expanded를 하면
RenderFlex children have non-zero flex but incoming height constraints are unbounded
오류가 발생합니다.
그 이유는
- 내부 Column의 크기 제약조건이 존재하지않는다.
- 내부 Column의 크기는 그 하위 위젯의 크기에 따라 결정된다.
- 내부 Column안에 Expanded 위젯은 부모의 크기제약에 따라 크기가 결정된다.
2번과 3번이 서로 크기 제약조건을 의존하고있는걸 확인할 수 있습니다. 따라서 오류가 발생하는것입니다.
이 같은 조건은 Row - Row에서도 동일하게 동작합니다.
ListView
ListView는 기본적으로 크기제약이 없고 가능한 모든 공간을 사용하려고 합니다. 제 기준 가장 사용하기 까다로운 위젯입니다. 왜냐하면 ListView를 단독으로 사용할일이 거의 없기 때문입니다.
보통 SingleChildScrollView 위젯내에서 사용하거나 Column 위젯에서 ListView를 사용하는데 오류가 가장 많이 나더라구요.
body: Column(
children: [
Container(
height: 50,
color: Colors.orange,
child: ...,
),
ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => Text('텍스트$index'),
),
Container(
height: 50,
color: Colors.orange,
child: ...,
),
],
),
이렇게 Column 안에 놓는다? Vertical viewport was given unbounded height. 바로 오류납니다.
위와 마찬가지로 ListView의 크기제약이 없어서 그렇습니다. 이 같은 상황에 해결법은 Expanded 위젯을 사용하는겁니다.
body: Column(
children: [
Container(
height: 50,
color: Colors.orange,
child: ...,
),
Expanded(
child: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => Text('텍스트$index'),
),
),
Container(
height: 50,
color: Colors.orange,
child: ...,
),
],
),
이제 오류가 안납니다. 그런데 보통 body에는 처음에 SingleChildScrollView 를 사용해서 전체 스크롤을 가능하게 해줍니다. 화면크기만으로는 컨텐츠를 몇개 못담으니까요.
body: SingleChildScrollView(
child: Column(
children: [
Container(
height: 50,
color: Colors.orange,
child: ...,
),
Expanded(
child: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) => Text('텍스트$index'),
),
),
Container(
height: 50,
color: Colors.orange,
child: ...,
),
],
),
)
RenderFlex children have non-zero flex but incoming height constraints are unbounded.
- SingleChildScrollView는 자식(Column)에게 높이 제약을 전달
- Column은 이 unbounded 제약을 받은 상태에서 Expanded를 만남, 즉 자식위젯들에게 높이 제약을 전달
- Expanded는 남은 공간을 차지하려고 하는데, unbounded 상태에서는 '남은 공간'이 얼마인지 계산할 수 없음
- 따라서 오류 발생
네 이럴때는 Expanded를 제거해야합니다. 하지만 여기에서 Expanded를 제거한다고 해결되지 않습니다.
Vertical viewport was given unbounded height.
오류가 발생합니다.
이도 ListView 첫번째 예제와 똑같이 생겼거든요. 그럼 어떻게 해결해야할까요?
shrinkWrap: true,
ListView에 속성중에 shrinkWrap 옵션이 있습니다. 해당 옵션은 false가 default인데, 이 옵션이 true가 되면 ListView가 자신의 자식들의 높이 합만큼만 크기를 가지도록 만듭니다. 그 말은 무한한 크기를 가지려는 성질을 제한하는 옵션이라는 겁니다. 이 옵션을 사용하면 SingleChildScrollView - Column - ListView 관계를 해결할 수 있습니다.
다만, 단점으로는 모든 자식의 높이를 계산해서 자신의 크기를 정해야 하기때문에 자식이 많을수록 성능저하가 발생할 수 있다는 겁니다. 따라서 사용할때는 주의를 갖고 사용해야합니다.
'Language > Flutter' 카테고리의 다른 글
[Flutter] 상태 관리 기초 - setState (0) | 2024.11.18 |
---|---|
[Dart] Spread 연산자(...) (0) | 2024.11.17 |
[Dart] Null Safety (0) | 2024.11.16 |
[Dart] var, dynamic, final, late, const 키워드 (0) | 2024.11.15 |
[Flutter] The binary version of its metadata is 1.8.0, expected version is 1.6.0 해결방법 (0) | 2024.09.19 |