Flutter/UI

[Flutter] CustomScrollView

찌김이 2022. 9. 6. 06:00
728x90
반응형

CustomScrollView 헤더의 확장, 축소 그리고 여러 개의 리스트뷰를 하나의 화면에 표현할 때 주로 사용합니다. 

 

 

특징

CustomScrollView 은 다른 위젯처럼 child 나 children 이 아닌 slivers 를 받습니다.

class SampleScreen extends StatelessWidget {
  const SampleScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          
        ],
      ),
    );
  }
}

 

slivers 는 List<Widget> 받는다고 되어있지만 그냥 위젯을 넣으면 아래와 같은 에러가 발생합니다.

아래의 상황은 slivers 내부에 Text 위젯을 넣었을 때 입니다.

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

 

 

slivers 내부에 RenderSliver 유형의 위젯을 기대했는데 RenderParagraph 유형의 위젯이 들어와있다는 내용입니다.

쉽게 생각해서 slivers 내부에는 대부분 Sliver 로 시작하는 위젯만 들어갈 수 있다고 생각하시면 될 것 같습니다.

 

 

주로 사용되는 RenderSliver 위젯은 다음과 같습니다.

 

 

SliverToBoxAdapter

  • 단일 위젯을 표현할 때 쓰는 sliver
SliverToBoxAdapter(
  child: Text('Text'),
)

 

 

SliverPadding

  • sliver 에 패딩을 적용하는 sliver
SliverPadding(
  padding: const EdgeInsets.all(10),
  sliver: SliverToBoxAdapter(
    child: Text('asdjfkljasdflk'),
  ),
)

 

 

SliverList

  • 리스트뷰를 표현하는 sliver 로 delegate 를 필수로 받습니다.
  • delegate 에 SliverChildListDelegate 를 넣어주고 List<Widget> 을 넣어주면 됩니다.
SliverList(
  delegate: SliverChildListDelegate(
    // List<Widget>
    [
      Text('내용1'),
      Text('내용2'),
      Text('내용3')
    ]  
  ),
)

 

 

 

  • 수평 리스트뷰를 구현하고 싶다면 SliverToBoxAdapter 내부에 리스트뷰로 구현하시면 됩니다.
SliverToBoxAdapter(
  child: ListView(
    scrollDirection: Axis.horizontal,
    children: [
      Text('수평1'),
      Text('수평2'),
      Text('수평3'),
    ],
  ),
)

 

 

SliverGrid

  • 그리드뷰를 표현하는 sliver 로 gridDelegate 와 delegate 를 필수값으로 받습니다.

 

  • delegate 를 SliverChildBuilderDelegate 이용하여 구현합니다.
  • gridDelegate 는 SliverGridDelegateWithFixedCrossAxisCount 받고 속성은 다음과 같습니다.
SliverGrid(
   delegate: SliverChildBuilderDelegate(
      (BuildContext context, int index) {
         return Container(
            color: Colors.red[100 * (index % 9)],
         );
      },
      childCount: 100,
   ),
   gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
      
      // 한 줄에 보일 내용의 수 - 필수값
      crossAxisCount: 2, 
      
      // GridView Item 간의 세로 간격
      crossAxisSpacing: 5,
      
      // GridView Item 간의 가로 간격
      mainAxisSpacing: 5, 
      
      // GridView Item 의 가로 길이
      mainAxisExtent: 200, 
    )
)

 

  • SliverGrid 는 SliverGrid.count, SliverGrid.extent 으로도 구현할 수 있습니다.
  • SliverGrid.count - GridView Item 의 크기에 상관없이 한줄에 표현되는 Item 의 수를 정하는 GridView 
  • SliverGrid.extent - GridView Item 의 크기를 고정시켜 한줄에 표현되는 Item 의 수를 동적으로 변경하는 GridView
SliverGrid.count(
  crossAxisCount: 2,
  // mainAxisSpacing: 0,
  // crossAxisSpacing: 0,
  children: [
    Container(color: Colors.red),
    Container(color: Colors.red[100]),
    Container(color: Colors.red[200]),
    Container(color: Colors.red[300]),
  ],
)
SliverGrid.extent(
  maxCrossAxisExtent : 200,
  // mainAxisSpacing: 0,
  // crossAxisSpacing: 0,
  children: [
    Container(color: Colors.red),
    Container(color: Colors.red[100]),
    Container(color: Colors.red[200]),
    Container(color: Colors.red[300]),
  ],
)

 

 

SliverAppBar

  • CustomScrollView 의 헤더를 확장, 축소할 때 쓰이는 AppBar 입니다. 
  • 여기에 적기엔 너무 내용이 많아서 따로 빼두겠습니다.

 

 

[Flutter] SliverAppBar

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

dalgoodori.tistory.com

 

SliverPersistentHeader

  • SliverAppBar 가 아닌 별도로 헤더를 pin 하거나 float 할 수 있는 위젯입니다.
  • 주로 SliverAppBar 아래에 탭바를 넣어서 많이 사용합니다.

 

 

  • SliverPersistentHeader 는 SliverPersistentHeaderDelete 를 받습니다. 
  • SliverPersistentHeaderDelete 를 상속받는 클래스를 만들어주고 maxExtent, 와 minExtent 를 정해줍니다.
  • 저는 예시에서 위젯을 받는 SliverPersistentHeaderDelete 로 만들었습니다.
class SampleHeaderDelegate extends SliverPersistentHeaderDelegate {
  SampleHeaderDelegate({required this.widget});

  Widget widget;

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return widget;
  }

  @override
  double get maxExtent => 50;

  @override
  double get minExtent => 50;

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
    return false;
  }
}

 

  • delegate 에 위에 만든 SampleHeaderDelegate 를 넣어주고 임의의 위젯을 넣어줍니다.
  • 상단에 고정시키고 싶다면 pinned 를 true, 스크롤을 아래로 내렸을 때 보이고 싶다면 floating 를 true 로 설정하시면 됩니다.
SliverPersistentHeader(
  delegate: SampleHeaderDelegate(
    widget: Container(
      height: 50,
      alignment: Alignment.center,
      color: Colors.blue,
      child: const Text(
        'SliverPersistentHeader',
        style: TextStyle(
          color: Colors.white,
          fontSize: 20
        ),
      ),
    ),
  ),
  pinned: true,
  // floating : false 
)

 

class SampleScreen extends StatelessWidget {
  const SampleScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: CustomScrollView(
      slivers: [
        SliverAppBar(
          flexibleSpace: FlexibleSpaceBar(
            background: Image.network(
              'https://cdn.pixabay.com/photo/2016/09/30/14/56/venetian-1705528_960_720.jpg',
              fit: BoxFit.cover,
            ),
            collapseMode: CollapseMode.none,
          ),
          expandedHeight: 200,
        ),
        SliverPersistentHeader(
          pinned: true,
          delegate: SampleHeaderDelegate(
            widget: Container(
              height: 50,
              alignment: Alignment.center,
              color: Colors.blue,
              child: const Text(
                'SliverPersistentHeader',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 20
                ),
              ),
            ),
          ),
        ),
        SliverList(
          delegate: SliverChildListDelegate(
            List.generate(
              100,
              (index) => Card(
                color: Colors.teal[100 * (index % 9)],
                child: Container(
                  padding: const EdgeInsets.all(20),
                  child: Text('Sample $index'),
                ),
              ),
            ),
          ),
        ),
      ],
    ));
  }
}

class SampleHeaderDelegate extends SliverPersistentHeaderDelegate {
  SampleHeaderDelegate({required this.widget});

  Widget widget;

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return widget;
  }

  @override
  double get maxExtent => 50;

  @override
  double get minExtent => 50;

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
    return false;
  }
}
728x90
반응형