이전에 이미 appBar를 구현했지만.. 스크롤 했을 때 appBar와 tabBar가 스크롤되지 않는 이슈로 가용 공간이 좁아지는 바람에 화면이 답답해져 NestedScrollView
를 이용해 처음부터 다시 구현하였다..🤦♀️
상단 AppBar
를 스크롤하는 움직임에 따라 변화를 주고 싶을 때 사용된다. 주로 CustomScrollview
의 자식 위젯으로 사용된다. 나는 스크롤 변화를 크게 줄 필요가 없어서 NestedScrollView
와 SliverAppBar
조합으로 사용했다.
📍 pinned
: 아래로 스크롤 했을 때 title
이 남아있는지 여부. 기본값은 false
📍 floating
: 화면 가장 위로 올리지 않더라도 스크롤을 위로 올리면 AppBar
가 나오는지 여부. 기본값은 false
📍 살짝만 스크롤 해도 AppBar
가 전체 확장되거나 축소되는지 여부. floating
이 true
일때만 사용 가능하다. 기본값은 false
→ 공식 문서에서 예시 코드를 통해 동작을 확인할 수 있다
주로 헤더에 TabBar
를 포함하고 body
에 TabBarView
를 포함할 때 사용된다. SliverAppBar
와 함께 사용하는 경우 어떻게 동작하는지 공식 문서에서 확인할 수 있다.
CustomScrollView
와 함께 사용하며, AppBar
등을 중첩되게 사용할 때 발생하는 문제를 해결할 때 사용한다... 라고 하는데 공식문서 설명이 그리 친절하지는 않아 정확하게는 모르겠다. 클로드에게 물어보니 중첩 시 발생하는 겹침 문제해결과 AppBar
와 컨텐츠 내용이 제대로 상호작용하도록 하는 것이 목적이라고 한다.
자식 위젯의 크기를 측정하여 스크롤뷰의 다른 부분이 AppBar
와 겹치지 않게 한다고 한다.
handle
: SliverOverlapAbsorberHandle
위젯을 넣는다. 이는 NestedScrollView
의 중첩된 부분을 이동시키는 핸들러이다.
나의 경우에는 탭 내부의 여백이 원하는대로 설정이 되지 않아(bottom 여백이 줄어들지 않음) SliverOverlapAbsorber
를 사용했다.
Tab을 사용하기 때문에 DefaultTabController
안에서 NestedScrollView
를 호출한다. (TabBar 정리글)
...
// 홈, 일정, 기록 탭 관리용
late TabController _tabController;
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
void dispose() {
_tabController.dispose();
super.dispose();
}
...
// body 내부 Container
child: DefaultTabController(
length: 3,
child: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return [
MyAppBar(
tabController: _tabController,
innerBoxIsScrolled: innerBoxIsScrolled,
),
];
},
body: TabBarView(
controller: _tabController,
children: const [
HomePage(),
SchedulePage(),
RecordPage(),
],
),
),
),
...
build(BuildContext context) {
return SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
title: Container(
padding: const EdgeInsets.all(20),
child: Text(
'안녕하세요 $_userName 님👏',
style: const TextStyle(
color: MyColors.myWhite,
fontSize: 24,
),
),
),
flexibleSpace: ClipRRect(
child: Container(
decoration: BoxDecoration(
gradient: MyTheme.tabBarGradient,
),
),
),
centerTitle: false,
pinned: true,
floating: true,
forceElevated: widget.innerBoxIsScrolled,
scrolledUnderElevation: 0,
backgroundColor: Colors.transparent,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(kTextTabBarHeight + 40),
child: Container(
padding: const EdgeInsets.only(bottom: 20.0),
height: kTextTabBarHeight + 40,
child: MyTabBar(tabController: widget.tabController),
),
),
),
);
}
Widget
Flutter 공식 문서: SliverAppBar class
Flutter 공식 문서: NestedScrollView class
Flutter 공식 문서: SliverOverlapAbsorber class
Flutter 공식 문서: SliverOverlapAbsorberHandle class