일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Firebase
- 파이어베이스
- listview
- 플러터
- Animation
- 함수
- AnimationController
- .NET
- 오류
- db
- Flutter
- MS-SQL
- JavaScript
- 마우이
- GitHub
- 리엑트
- HTML
- 닷넷
- MSSQL
- spring boot
- page
- 깃허브
- MVVM
- typescript
- React JS
- Binding
- 자바스크립트
- Maui
- 바인딩
- 애니메이션
Archives
- Today
- Total
개발노트
35. [Flutter] NestedScrollView 로 여러개 스크롤 하나로 제어하기(with Sliver) 본문
앱 개발/Flutter
35. [Flutter] NestedScrollView 로 여러개 스크롤 하나로 제어하기(with Sliver)
mroh1226 2023. 10. 11. 16:27반응형
NestedScrollView
개발하다보면 각각의 위젯마다 스크롤이 따로되는 위젯들을 마주하게됩니다. 이를 하나의 스크롤로 묶어서 제어하고 싶을 때 NestedScrollView를 사용하면됩니다.
- NestedScrollView는 Flutter에서 사용되는 스크롤 가능한 위젯의 중첩된 구조를 만들기 위한 유용한 위젯입니다. 이것은 여러 슬리버 (Sliver) 위젯과 스크롤 가능한 컨텐츠를 함께 사용하여, 복잡한 스크롤 레이아웃을 만들 수 있게 해줍니다. 주로 스크롤 가능한 헤더와 본문을 함께 사용하여 구현됩니다.
- NestedScrollView는 머티리얼 디자인 앱에서 많이 사용되며, 다음과 같은 주요 구성 요소로 구성됩니다:
- Header Slivers: 이 부분에는 앱의 헤더 또는 헤더와 관련된 위젯이 포함됩니다. 주로 SliverAppBar와 같은 슬리버 위젯을 포함합니다. 헤더는 스크롤 가능한 컨텐츠 위에 놓이며, 일반적으로 pinned 속성을 사용하여 상단에 고정할 수 있습니다.
- Body: 이 부분에는 본문 컨텐츠가 포함됩니다. 본문은 스크롤 가능한 목록 또는 컨텐츠가 될 수 있으며, 주로 TabBarView와 같은 슬리버 위젯을 포함합니다.
- Custom Slivers: NestedScrollView는 헤더와 본문 사이에 사용자 지정 슬리버 위젯을 추가하는 것도 허용합니다. 이를 통해 복잡한 레이아웃을 만들 수 있습니다.
- NestedScrollView를 사용하면 앱에서 다양한 레이아웃을 구성할 수 있으며, 본문 스크롤과 헤더 고정을 조합하여 멋진 사용자 경험을 제공할 수 있습니다. 이것은 특히 탭 바와 함께 사용할 때 유용하며, 각 탭에 대한 서로 다른 슬리버와 본문을 만들 수 있습니다.
NestedScrollView를 구현할 때 주의할 점은 스크롤에 대한 동작 및 레이아웃을 정확하게 구성해야 한다는 것입니다. 위젯의 중첩 및 각 슬리버와 본문의 크기 및 위치를 조절해야 합니다.
예시.
NestedScrollView를 이용하여 SliverAppBar, SliverPersistentHeader, SliverToBoxAdapter 와 함께 GridView 스크롤을 하나로 통제하는 기능을 만들어보겠습니다.
(2개의 스크롤을 1개의 스크롤 행위로 통합하기)
import 'package:flutter/material.dart';
class TestScreen extends StatefulWidget {
const TestScreen({super.key});
@override
State<TestScreen> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: DefaultTabController(
length: 2,
child: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
const SliverAppBar(
elevation: 0,
centerTitle: true,
title: Text(
'SliverAppBar',
style: TextStyle(color: Colors.black),
),
),
const SliverToBoxAdapter(
child: Column(
children: [
CircleAvatar(
radius: 60,
),
Text(
'Avatar',
style: TextStyle(
fontSize: 28, fontWeight: FontWeight.bold),
),
],
),
),
SliverPersistentHeader(
pinned: true, delegate: SliverDelegate())
];
},
body: TabBarView(
children: [
GridView.builder(
itemCount: 12,
padding: EdgeInsets.zero,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: 1,
mainAxisSpacing: 1,
childAspectRatio: 9 / 16,
crossAxisCount: 3),
itemBuilder: (context, index) => Container(
alignment: Alignment.center,
color: Colors.pink.shade200,
child: Text('첫번째 Tab $index'),
),
),
GridView.builder(
itemCount: 12,
padding: EdgeInsets.zero,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: 1,
mainAxisSpacing: 1,
childAspectRatio: 9 / 16,
crossAxisCount: 3),
itemBuilder: (context, index) => Container(
alignment: Alignment.center,
color: Colors.green.shade300,
child: Text('두번째 Tab $index'),
),
),
],
)),
),
),
);
}
}
class SliverDelegate extends SliverPersistentHeaderDelegate {
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
//Container 안에 decoration을 정해줘야 pinned = true로 해도 오류가 안남
decoration: BoxDecoration(
color: Colors.white,
border: Border.symmetric(
horizontal: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
),
child: const TabBar(
indicatorSize: TabBarIndicatorSize.tab,
indicatorColor: Colors.black,
labelPadding: EdgeInsets.symmetric(
vertical: 10,
),
labelColor: Colors.black,
tabs: [
Padding(
padding: EdgeInsets.symmetric(
horizontal: 20,
),
child: Icon(Icons.grid_4x4_rounded),
),
Padding(
padding: EdgeInsets.symmetric(
horizontal: 20,
),
child: Icon(Icons.favorite_rounded),
),
],
),
);
}
@override
double get maxExtent => 47;
@override
double get minExtent => 47;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return true;
}
}
설명.
화면 부분
- Scaffold 및 SafeArea:
Scaffold 위젯은 기본 앱 스켈레톤을 제공합니다.
SafeArea는 화면의 안전한 영역에 컨텐츠를 배치하며, 일반적으로 노치 또는 상단 바와 같은 장치의 경계를 고려합니다. - DefaultTabController:
DefaultTabController는 탭 뷰를 관리하며, length 속성에 2가 설정되어 두 개의 탭을 가집니다. - NestedScrollView:
NestedScrollView는 스크롤 가능한 헤더와 본문을 포함하는 중첩된 스크롤 레이아웃을 생성합니다.
headerSliverBuilder 콜백에서 슬리버 위젯 및 헤더를 정의합니다.
body: 에는 또 다른 스크롤 이 필요한 GridView를 자식으로 갖는 TabBarView와 탭에 해당하는 본문이 정의됩니다. - SliverAppBar:
SliverAppBar는 헤더 부분을 나타냅니다.
title에 'SliverAppBar' 텍스트가 표시되며, centerTitle 및 elevation 설정을 사용하여 디자인을 조정합니다. - SliverToBoxAdapter:
SliverToBoxAdapter는 헤더 아래에 있는 추가 정보를 표시하며, CircleAvatar와 'Avatar' 텍스트가 포함됩니다. - SliverPersistentHeader:
SliverPersistentHeader는 탭 바를 pinned = true로 설정하여 고정된 헤더로 표시합니다.
delegate로 따로 생성한 SliverDelegate 클래스가 사용됩니다. - TabBarView:
TabBarView는 탭 바와 연결된 본문을 표시합니다.
두 개의 탭이 있으며, 각 탭에는 GridView.builder가 포함되어 각각 '첫번째 Tab' 및 '두번째 Tab'을 가진 그리드가 표시됩니다.
SliverDelegate()
- SliverPersistentHeader 에 사용될 SliverPersistentHeaderDelegate를 따로 생성해줍니다.
- 이때 maxExtent에는 Header가 확장되었을 때 크기를 minExtent에는 축소되었을 때 크기를 입력해줍니다.
위와 같이 NestedScrollView를 사용하면 헤더와 본문을 함께 스크롤할 수 있으며, 헤더를 고정하고 탭 바를 표시하는 등 다양한 스크롤 레이아웃을 구현할 수 있습니다.
위 예시를 만들면서 NestedScrollView 위젯 적용에 아래와 같은 어려움이 있었으며, 여러 방법으로 시도한 끝에 아래와 같은 해결 방법을 알아냈습니다.
문제
- TabBar가 들어있는 SliverPersistenHeader에 pinned = true로 설정하여 스크롤시 고정되는 기능을 만들려고 했으나..
- 레이아웃 오류가 뜸
원인
- SliverPersistentHeader 안에 있는 TabBar를 그대로 자식으로 넣으니, TabBar 쪽에서 오류가 남
- delegate로 사용된 SliverPersistentHeaderDelegate의 maxExtent, minExtent 수치를 정확하게 입력하지않음
해결 방법
- TabBar를 Container() 위젯으로 감싸줌
- decoration: 속성에 BoxDecoration()을 넣어 줌
*decoration을 설정하지 않으면 오류남 - Delegate로 사용한 SliverPersistentHeaderDelegate의 maxExtent, minExtent를 47로 설정함(오류 메시지에서 수치 확인)
빌드된 모습.
반응형
'앱 개발 > Flutter' 카테고리의 다른 글
37.[Flutter] LayoutBuilder 로 위젯 크기 가져오기 (vs MediaQuery) (2) | 2023.10.13 |
---|---|
36. [Flutter] ListWheelScrollView 회전하는 리스트뷰 만들기 (2) | 2023.10.11 |
34. [Flutter] CustomScrollView 위젯 (2) | 2023.10.05 |
33. [Flutter] AnimatedList 로 쉽게 List 애니메이션 효과 구현하기 (with GlobalKey) (0) | 2023.09.22 |
32. [Flutter] AnimationController 애니메이션 적용 방법 2가지 (with RotationTransition, Tween) (0) | 2023.09.15 |
Comments