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 입니다.
- 여기에 적기엔 너무 내용이 많아서 따로 빼두겠습니다.
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
반응형