본문 바로가기
에러 해결방법

[트러블슈팅] ListView 안에 ListeView(Feat. CustomScrollView)

by 개발짜 2024. 11. 26.

문제 상황

여러 개의 ListView 를 한 화면에 넣고 싶다. 아래와 같이 하나는 row 처럼 하나는 column 처럼 스크롤하고 싶다. 하지만 ListView 안에 ListView 를 사용하면 화면은 안 나타나고 오류가 난다.

내가 원하는 것

 

문제

The following assertion was thrown during performResize(): Vertical viewport was given unbounded height.

무제한으로 수직 높이가 커져서 오류가 발생한다.

 

시도했던 해결 방법

각 ListView 들을 Expanded 또는 Flexible 과 같은 위젯으로 감싸기 => 실패

막연하게 이전에 Column 데이터를 ListView 로 바꿀 때 처럼 사이즈를 주지 않고 반응형 위젯으로 알아서 해줄줄 알았지만 오류가 발생했다. Expanded 위젯은 Column, Row, Flex 내부에서만 사용할 수 있기 때문에 부모 위젯이 ListView 인 이 경우에는 해결이 안된다.

 

해결1 - ListView 를 SizedBox 로 감싸고 height 지정

구조는 다음과 같다.

- ListView
    - SizedBox
        - ListView // 수평
    - widget
    - widget
    - widget
    - ...

 

코드로 작성하면 다음과 같다.

ListView(
  children: [
    SizedBox(
      // 높이 지정하면 오류 발생 안함
      height: 50,
      child: ListView(
        scrollDirection: Axis.horizontal, // 수평
        children: [
          Text('RowWidget1'),
          Text('RowWidget2'),
          Text('RowWidget3'),
          Text('RowWidget4'),
          Text('RowWidget5'),
        ],
      ),
    ),
    // 수직
    Text('ColumnWidget1'),
    Text('ColumnWidget2'),
    Text('ColumnWidget3'),
    Text('ColumnWidget4'),
    Text('ColumnWidget5'),
    Text('ColumnWidget6'),
  ],
)

 

해결2 - CustomScrollView 의 사용

ListView 안에 ListView 를 사용하는 방법을 서치하는 도중 CustomScrollView 위젯을 발견했다. CustomScrollView 는 다양한 스크롤을 할 수 있는 위젯을 구성할 수 있고, 각각의 sliver 위젯은 독립적으로 렌더링되기 때문에 성능이 좋다.

 

사용 방법

CustomScrollView 는 자식 위젯을 slivers 로 지정한다.

CustomScrollView(
  slivers: [
    // 위젯1
    // 위젯2
    ...
  ],
)

 

하지만 slivers 에 바로 ListView 를 추가하면 다음과 같은 오류가 발생한다.

CustomScrollView(
  slivers: [
    ListView(),
    ListView(),
    ...
  ],
)

 

 

A RenderViewport expected a child of type RenderSliver but received a child of type _RenderScrollSemantics.

 

slivers 는 List<widget> 타입으로 반환을 받는다고 써져있지만 실제로는 RenderSliver 유형의 child 만 반환을 받는다. 즉, Sliver 로 시작하는 위젯들을 쭉 나열하면 된다.

RenderSliver 유형 위젯 예시

 

나는 SliverToBoxAdapter 으로 수평 리스트뷰를 만들고 SliverList 를 이용해서 수직 리스트를 만들 것이다.

SliverToBoxAdapter 는 단일 자식을 가지는 위젯으로, 수평으로 scroll 하는 ListView 을 자식 위젯으로 한다.

SliverList 는 ListView 처럼 여러 자식을 갖는 위젯으로 delegate 속성에 SliverChildListDelegate 내부에서 여러 위젯을 나열하면 된다.

CustomScrollView(
  slivers: [
    // 수평 리스트뷰
    SliverToBoxAdapter(
      child: ListView(
        scrollDirection: Axis.horizontal,
        children: [
          Text('RowWidget1'),
          Text('RowWidget2'),
          Text('RowWidget3'),
          Text('RowWidget4'),
        ],
      ),
    ),
    // 수직 리스트뷰
    SliverList(
      delegate: SliverChildListDelegate(
        [
          Text('ColumnWidget1'),
          Text('ColumnWidget2'),
          Text('ColumnWidget3'),
          Text('ColumnWidget4'),
        ]  
      ),
    )
  ]
)

 

하지만 위와 같이 작성하게 되면 또 다시 오류 지옥에 빠지게 된다.

Horizontal viewport was given unbounded height.
Viewports expand in the cross axis to fill their container and constrain their children to match their extent in the cross axis. In this case, a horizontal viewport was given an unlimited amount of vertical space in which to expand.

 

수평 viewport 가 높이가 무한으로 확장되어서 화면 바깥으로 나갈 때 발생하는 오류로, ListView 를 Expanded 로 감싸주고 싶지만 위에 써놓은 거 처럼 부모 위젯이 Column, Row, Flex 가 아니므로 해당 방법은 적절치 않다. 

 

따라서 나는 수평 ListView 를 SingleChildScrollView 로 변경하고 자식 위젯을 Row 하여 리스트처럼 보여지게 했다.

CustomScrollView(
  slivers: [
    // 수평 리스트뷰
    SliverToBoxAdapter(
      // 리스트뷰를 SingleChildScrollView 로 변경
      child: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        // 
        child: Row(
          children: [
            Text('RowWidget1'),
            Text('RowWidget2'),
            Text('RowWidget3'),
            Text('RowWidget4'),
          ]),
      ),
    ),
    // 수직 리스트뷰
    SliverList(
      delegate: SliverChildListDelegate(
        [
          Text('ColumnWidget1'),
          Text('ColumnWidget2'),
          Text('ColumnWidget3'),
          Text('ColumnWidget4'),
        ]  
      ),
    )
  ]
)

 

예시 화면

 

CustomScrollView 사용법 참고 - https://dalgoodori.tistory.com/62

 

[Flutter] CustomScrollView

CustomScrollView 헤더의 확장, 축소 그리고 여러 개의 리스트뷰를 하나의 화면에 표현할 때 주로 사용합니다. 특징 CustomScrollView 은 다른 위젯처럼 child 나 children 이 아닌 slivers 를 받습니다. class Sample

dalgoodori.tistory.com

 

 

댓글