개요

Flutter에는 부모위젯으로부터 제약을 받아 자신의 크기를 결정하는 위젯들이 존재합니다.

만약 부모의 크기가 정해지지 않다면 자신의 크기를 정하지 못해 오류가 발생합니다. 이에 대해서 알아봅시다.

 

 

Column - Column - (Expanded, Flexible)

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를 넣으면 깔끔하게 채워줍니다. 이게 가능한 이유는

  1. Column / Row 는 부모로부터 제약조건을 받아야한다. 없다면 하위 위젯에 크기에 따라서 정해진다.
  2. Scaffold 의 body사이즈는 MediaQuery의 크기(화면 크기)에서 AppBar, BottomNavigationBar등이 차지하는 공간을 제외한 나머지 영역의 크기로 정해져있어 크기 제약조건이 존재한다.
  3. 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

오류가 발생합니다.

 

그 이유는

  1. 내부 Column의 크기 제약조건이 존재하지않는다.
  2. 내부 Column의 크기는 그 하위 위젯의 크기에 따라 결정된다.
  3. 내부 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.

 

 

  1. SingleChildScrollView는 자식(Column)에게 높이 제약을 전달
  2. Column은 이 unbounded 제약을 받은 상태에서 Expanded를 만남, 즉 자식위젯들에게 높이 제약을 전달
  3. Expanded는 남은 공간을 차지하려고 하는데, unbounded 상태에서는 '남은 공간'이 얼마인지 계산할 수 없음
  4. 따라서 오류 발생

네 이럴때는 Expanded를 제거해야합니다. 하지만 여기에서 Expanded를 제거한다고 해결되지 않습니다.

 

Vertical viewport was given unbounded height.

오류가 발생합니다.

 

이도 ListView 첫번째 예제와 똑같이 생겼거든요. 그럼 어떻게 해결해야할까요?

 

shrinkWrap: true,

 

ListView에 속성중에 shrinkWrap 옵션이 있습니다. 해당 옵션은 false가 default인데, 이 옵션이 true가 되면 ListView가 자신의 자식들의 높이 합만큼만 크기를 가지도록 만듭니다. 그 말은 무한한 크기를 가지려는 성질을 제한하는 옵션이라는 겁니다. 이 옵션을 사용하면 SingleChildScrollView - Column - ListView 관계를 해결할 수 있습니다.

 

다만, 단점으로는 모든 자식의 높이를 계산해서 자신의 크기를 정해야 하기때문에 자식이 많을수록 성능저하가 발생할 수 있다는 겁니다. 따라서 사용할때는 주의를 갖고 사용해야합니다.

 

 

+ Recent posts