현재 저희 프로젝트에선 ListVeiw를 주로 사용하고 있는데 Flutter에서 SliverList라는 위젯을 제공하고 있어서 무엇인지 궁금하여 차이가 무엇인지 찾아보게 되었습니다.
결론적으로 이 둘의 차이를 비교하는 건 의미가 없었습니다.
이번 글에서는 ListView와 SliverList의 차이가 아닌 Sliver의 전반적인 설명이 담긴 영상을 요약 가이드하는 글 입니다. 더 자세히 알고 싶으시면 영상을 봐주시면 감사하겠습니다. (00:00 ~ 20:44)
Sliver를 이해하기 위해선 일반적으로 Flutter가 레이아웃을 어떻게 처리하는지 알면 도움이 됩니다.
Flutter엔 render object가 있습니다. Render object는 대부분 Render boxes(ex. Container, SizeBoxes)에 표시됩니다.
💡Constraints go down. Sizes go up. Parent sets position.
render boxes가 배치될 땐 부모로 부터 constraints(제약 조건)를 가져옵니다. constraints는 minimum and maximum width와 minimum and maximum height로 구성됩니다. constraints를 구성한 다음 children을 하나씩 통과하면서 children이 가져야할 constraints를 알려주고 children이 원하는 사이즈를 얻어옵니다. children은 constraints**를 통해 가능한 사이즈를 확인하고 배치된 후 부모에게 알려줍니다.
참고: https://docs.flutter.dev/development/ui/layout/constraints
이러한 레이아웃 배치 방식은 Render box에 적합합니다. 하지만 스크롤이 일어나는 위젯에겐 적합하지 않습니다. 그래서 이들은 Sliver protocol을 사용합니다. 자세한 내용은 RanderSliver Docs에서 확인하실 수 있습니다.
💡 each sliver receives a SliverConstraints object and computes a corresponding SliverGeometry that describes where it fits in the viewport
각 Sliver는 SliverConstraints를 받아 뷰포트에 맞는 위치에 그려내는 SliverGeometry를 계산합니다.
Render box에서 사용되던 BoxConstraints 대신 SliverConstraints을 활용합니다.
SliverConstraints는 constraints를 넘겨 받고 있는 Sliver의 관점에서
뷰포트의 현재 스크롤 상태를 그려냅니다.
All of these properties are the incoming constraints for a Sliver
SliverGeometry는 Render box에서 사용하는 protocol과 유사합니다. Sliver는 여러가지 다른 방법으로 공간을 차지할 수 있으므로 SliverGeometry에는 여러가지 값이 포함됩니다.
Sliver가 얼마나 큰지, 다음 Sliver가 어디에 배치 되어야 할지, 뷰포트 밖의 아이템을 얼마나 caching 해야 할지 등을 설정할 수 있습니다.
Pretty much everything that scrolls in Flutter is using Sliver
passing constraints down and then geometry back up.
Render box의 경우 부모로 부터 제약 조건을 받아 사이즈를 알려주지만 Sliver의 경우 모든걸 결정할 수 있다.
for a box, I(child) only take constraints and then I(child) give you(parent) offset and size.(to parents). But for Slivers, you get all these things, and then you can decide whatever.
그 이유는 얼마나 스크롤 되었는지, 그래서 얼마나 커져야하는지, 그 다음 Sliver가 어디에 위치해있는지 알아야하기 때문이다.
(진짜 영상에서 한 말입니다)
SliverAppBar를 사용하기 위해선 CustomScrollView로 감싸줘야합니다. Scaffold 대신 CustomScrollView를 사용하는 이유는 스크롤이 필요하기 때문입니다. Scaffold가 필요하다면 사용 가능합니다. 하지만 CustomScrollView의 밖에 있는 Scaffold에 넣는다면 스크롤이 작동하지 않을 것 입니다. (CustomScrollView는 여러 자식을 가질 수 있는데 보통 사용하는 children 대신 slivers를 사용합니다. )
code
CustomScrollView(
slivers: <Widget>[
const SliverAppBar(
pinned: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('Demo'),
),
),
SliverList는 ListView와 동일한 arguments를 가집니다. 왜냐하면 ListView는 CustomScrollview + SliverList로 만들어졌기 때문입니다. SliverList는 크게 2가지 방식으로 위젯을 그려냅니다.
SliverChildBuilderDelegate(builder)
ListView.builder와 같은 방식. 인자로 들어가는 index값이 childCount보다 작은 동안 계속 실행되어 목록을 만드는 것.
SliverChildListDelegate(children)
실제 위젯 리스트를 갖고있는 방식. Sliver에 그려낼 아이템을 직접 넣어줘야 함.
1번, 2번 모두 궁극적으로 Render box를 렌더링 하진 않지만
2번 방식은 위젯 리스트 자체를 넘겨주어 위젯 리스트 전체를 생성해야하기 때문에 위젯을 인스턴스화 합니다.
1번 방식을 사용하여 특정 시간에 필요한 위젯만 빌드할 수 있습니다.
they don’t instantiate the render boxes in advance, but the List version does instantiate the Widgets, because you create the whole list of the Widgets. You can actually use the BuilderDelegate to build just the Widgets that it needs that particular time.
꼭 명시를 해주지 않더라도 builder가 null을 반환하면 last child라고 가정합니다.
그럼에도 명시를 해주는 이유는 리스트의 길이를 예측하여 스크롤 막대를 사용할 수 있도록 할 수 있습니다.
code
CustomScrollView(
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => _buildTilte(context, allNames[index]),
childCount: allNames.length,
)
)
],
)
SliverPadding으로 SliverList를 감쌀 수 있습니다. Render box에서 사용되는 Padding과 매우 유사합니다.
물론 SliverAppBar를 감쌀 수도 있습니다.
Slivers on the outside, but on the inside they take boxes(Render box).
ex. SliverList(Sliver) ⇒ build Container(Box)
Slivers that take Slivers on the outside and Slivers on the inside.
ex. SliverPadding(Sliver) ⇒ SliverList(Sliver)
code
CustomScrollView(
slivers: [
SliverPadding(
padding: EdgeInsets.all(16),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => _buildTilte(context, allNames[index]),
childCount: allNames.length,
)
)
)
]
)
20:44 이후 영상을 시청해주세요
가장 간단한 Sliver인 SliverToBoxAdapter가 제일 쉬운 방법이니깐 소스코드를 보고 참고하여 새 class 만들고 원하는 부분을 수정하면서 실험합니다.
참고한 벨로그: https://velog.io/@adbr/flutter-Sliver-설명-정리본-Slivers-explained-Making-dynamic-layouts