Flutter Scroll Widgets

Flutter Scroll Widgets

Tag
Flutter
플러터에서 쓰이는 scroll 관련 위젯 중 기본적으로 많이 쓰이는 위젯들만 정리해봤다.
( 내가 나중에 보고 쓸 목적으로다가.. )
📌
Column내부에 ScrollView를 넣을 때는 Expanded를 사용해야 함. 개념 상 height는 무한하기 때문

SingleChildScrollView

화면에 보이지 않는 위젯도 렌더링
 
  • 보통 사용할 때에는 SingleChildScrollView의 child에 Column을 넣어서 사용한다.
  • child의 크기가 화면을 넘어가지 않으면 스크롤이 되지 않으며 화면을 넘어갔을 때만 스크롤을 할 수 있다
 

속성

  • physics: SingleChildScrollView의 스크롤이 어떻게 작용을 하는지 정할 수 있다
    • 기본값: NeverScrollableScrollPhysics
    • 화면을 넘어가지 않았을 때도 스크롤: AlwaysScrollableScrollPhysics
    • 화면 위로 스크롤 했을 때 튕기는: BouncingScrollPhysics
      • ios의 기본 설정
    • 튕기지 않음: ClampingScrollPhysics
      • Android의 기본 설정
    • 캐러셀 처럼 작동 : PageScrollPhysics
  • clipBehavior: 스크롤이 될때 child의 크기만큼만 height를 가지고 있기 때문에 내용이 잘리게 되는데 이와 관련된 설정 가능
    • Clip.antiAlias, Clip.antiAliasWithSaveLayer, Clip.hardEdge: 기본 값
      • 다 잘림. 모두 퍼포먼스 차이
    • Clip.none: 잘리지 않는다
 

ListView

위에서 아래로 위젯들을 배치!
notion image
  • ListView 를 바로 사용?
    • 모든 위젯 렌더링
  • ListView.builder
    • 보이는 화면들만 렌더링
  • ListView.separated
    • 보이는 화면들만 렌더링
    • 위젯 사이 사이마다 위젯 render 가능
 

속성

  • scrollDirection: 스크롤의 방향 설정 ( 말그대로 😉 )
  • children: Widget[]
ListTile, CheckboxListTile 같은 친구들을 쓸 수 있다~
 

GridView

Grid 형식으로 리스트들을 배치!
notion image
 
  • GridView.count
    • 모든 위젯 렌더링
    •  

속성

  • crossAxisCount: row 마다 위젯을 몇 개를 넣을 것이냐
  • crossAxisSpacing: column gap
  • mainAxisSpacing: row gap
 
  • GridView.builder
    • ⇒ 보이는 화면만 위젯 렌더링
       

속성

  • gridDelegate : SliverGridDelegate
 
SliverGridDelegate 종류
  • SliverGridDelegateWithFixedCrossAxisCount
    • 개수를 정할 수 있음
  • SliverGridDelegateWithMaxCrossAxisExtent
    • 위젯의 최대 크기에 따라서 자동으로 개수 조정
 

ReorderableListView

📌
oldIndex와 newIndex 모두 이동하기 전 기준으로 산정한다. ⇒ dnd 가능한 위젯들을 나열하기 때문에 dnd가 끝난 이후 oldIndex, newIndex를 받아서 order를 만들기 때문
 

속성

  • onReorder
 
ex) 기준 [red(0), orange(1), yellow(2)]
red를 yellow 아래로 옮기고 싶은 경우
red의 oldIndex 0 ⇒ newIndex 3
 
yellow를 맨 위으로 옮기고 싶은 경우
yellow의 oldIndex 2 ⇒ newIndex 0
 
// List<int> numbers = List.generate(100, (index) => index); // const RAINBOW_COLORS = [ // Colors.red, // Colors.orange, // Colors.yellow, // Colors.green, // Colors.blue, // Colors.indigo, // Colors.purple // ]; body: ReorderableListView.builder( itemCount: numbers.length, itemBuilder: (context, index) { return renderContainer( color: RAINBOW_COLORS[numbers[index] % RAINBOW_COLORS.length], index: numbers[index]); }, onReorder: ((oldIndex, newIndex) { setState(() { // 위에서 아래로 옮겼을 때 if (oldIndex < newIndex) { newIndex -= 1; } final item = numbers.removeAt(oldIndex); numbers.insert(newIndex, item); }); }), ),
 

CustomScrollView

예를 들어 한 Column 내부에 Expanded로 감싼 ListView, Expanded로 감싼 GridView가 있을 경우, ListView를 다 스크롤하고 나서 GridView가 뜨는 것을 기대했다면, 전혀 그렇게 작동되지 않는다.
화면의 반을 ListView가, 나머지 반을 GridView가 먹게 된다.
notion image
이것이 아닌, ListView를 다 스크롤하고 나서 GridView가 뜨는 것을 구현하고 싶다면 바로 그때 CustomScrollView 위젯을 사용하면 된다.
 

속성

  • slivers: 가능한 scroll 위젯들을 넣을 수 있음 ( children 이라고 생각하면 편하다 )
    • Sliver이 붙은 위젯들만 써라.. 아니면 오류 뿜뿜
 
그렇다면 slivers에 어떤 sliver 위젯들을 쓸 수 있는지 정리해보자.

SliverAppBar

보면 알겠지만 걍 AppBar다.

속성

  • floating: true
    • ⇒ 스크롤 올릴 때 헤더 나오는 형식
  • pinned: true
    • => 웹에서 position: fixed 와 같은 기능이라고 생각하면 편함
  • snap: true
    • floating의 중간이 없도록 함
    • floating: true와 써야한다. 생각해보면 당연하다.
  • stretch: true
    • 한계 이상까지 상단으로 스크롤 했을 경우 scaffold의 배경색이 보이는게 아닌, appbar가 보임
  • expandedHeight: double
    • 앱바의 최대 크기 ( 아래로 뚱뚱해진다 )
  • collapsedHeight: double
    • expandedHeight에서 상단 스크롤시 최소의 앱바 height ( 평소보다 크겠지? )
  • flexibleSpace: FlexibleSpaceBar
    • appbar 아래에 위젯들을 넣을 수 있음
 

SliverList

ListView의 Sliver 위젯 버젼이다.

속성

  • delegate
    • SliverChildListDelegate ⇒ 모든 위젯 렌더링
    • SliverChildBuilderDelegate ⇒ 보이는 위젯만 렌더링
 

SliverGrid

말안해도 알겠지만 GridView의 Sliver위젯 버젼이다.
  • delegate
    • SliverChildListDelegate
      • 모든 위젯 렌더링
    • SliverChildBuilderDelegate
      • 보이는 위젯만 렌더링
  • SliverToBoxAdapter
    •  

SliverPersistentHeader

CustomScrollView에 꼭 위젯들을 나열하는게 아닌 각 섹션의 헤더 역할을 하는 위젯을 만들고 싶은 경우가 생길 것이다. 그때 사용하면 되는 위젯.

속성

  • pinned: sticky 개념
  • delegate: SliverPersistentHeaderDelegate
    • 다른 delegate 속성들처럼 바로 넣는 것이 아닌 class를 만들어서 넣어야한다.
 
class _SliverFixedHeaderDelegate extends SliverPersistentHeaderDelegate { final Widget child; final double maxHeight; final double minHeight; _SliverFixedHeaderDelegate({ required this.child, required this.maxHeight, required this.minHeight, }); @override Widget build( BuildContext context, double shrinkOffset, bool overlapsContent) { return SizedBox.expand(child: child); } @override // MEMO: 최대 높이 double get maxExtent => maxHeight; @override // MEMO: 최소 높이 double get minExtent => minHeight; @override // MEMO: covariant => 상속된 클래스도 사용 가능 ( SliverPersistentHeaderDelegate를 상속한 클래스도 될 수 있다 ) // MEMO: oldDelegate => build가 실행이 됐을 때 이전 Delegate // MEMO: this => 새로운 delegate // MEMO: shouldRebuild => 새로 build를 해야 할지 말지 결정 ( true일 경우 빌드 다시 한다 ) bool shouldRebuild(_SliverFixedHeaderDelegate oldDelegate) { return oldDelegate.minHeight != minHeight || oldDelegate.maxHeight != maxHeight || oldDelegate.child != child; } }
 

Scrollbar

위에서 적은 모든 scroll 관련 위젯들은 모두 스크롤바가 뜨지 않는다.
그래서 스크롤바를 띄우고 싶다면 scroll 관련 위젯을 Scrollbar 위젯으로 감싸면 된다.
 

RefreshIndicator

당신이 앱을 사용했다면 겪었을 상황이겠지만, 보통 스크롤이 가능한 스크린에서 맨 위로 화면을 땡기게 되면 refresh 할 수 있게 되었을 것이다.
Scrollbar 위젯과 같이 scroll 관련 위젯을 RefreshIndicator 위젯으로 감싸면 된다.
notion image

속성

  • onRefresh
    • typedef RefreshCallback = Future<void> Function();

SliverToBoxAdapter

예를 들어 Sliver 위젯이 아닌 Text 위젯을 꼭 CustomScrollView 위젯 내부에 넣어야겠다! 싶을 때 쓰면 된다. 단일 위젯을 표현할 때 쓴다고 생각하자.
SliverToBoxAdapter( child: Text('Text'), )

SliverPadding

딱 봐도 Padding 위젯의 Sliver 버젼이다.
SliverPadding( padding: const EdgeInsets.all(12.0), sliver: SliverToBoxAdapter( child: Text('Text'), ), )
 

References