[Flutter] NestedScrollView + SliverAppBar

zinnnn37·2024년 7월 26일
0

🎯 Flutter

목록 보기
7/8

이전에 이미 appBar를 구현했지만.. 스크롤 했을 때 appBar와 tabBar가 스크롤되지 않는 이슈로 가용 공간이 좁아지는 바람에 화면이 답답해져 NestedScrollView를 이용해 처음부터 다시 구현하였다..🤦‍♀️

🍫 SliverAppBar

상단 AppBar를 스크롤하는 움직임에 따라 변화를 주고 싶을 때 사용된다. 주로 CustomScrollview의 자식 위젯으로 사용된다. 나는 스크롤 변화를 크게 줄 필요가 없어서 NestedScrollViewSliverAppBar 조합으로 사용했다.

🪶 주요 속성

📍 pinned: 아래로 스크롤 했을 때 title이 남아있는지 여부. 기본값은 false
📍 floating: 화면 가장 위로 올리지 않더라도 스크롤을 위로 올리면 AppBar가 나오는지 여부. 기본값은 false
📍 살짝만 스크롤 해도 AppBar가 전체 확장되거나 축소되는지 여부. floatingtrue일때만 사용 가능하다. 기본값은 false
공식 문서에서 예시 코드를 통해 동작을 확인할 수 있다

🪺 NestedScrollView

주로 헤더에 TabBar를 포함하고 bodyTabBarView를 포함할 때 사용된다. SliverAppBar와 함께 사용하는 경우 어떻게 동작하는지 공식 문서에서 확인할 수 있다.

🧽 SliverOverlapAbsorber

CustomScrollView와 함께 사용하며, AppBar 등을 중첩되게 사용할 때 발생하는 문제를 해결할 때 사용한다... 라고 하는데 공식문서 설명이 그리 친절하지는 않아 정확하게는 모르겠다. 클로드에게 물어보니 중첩 시 발생하는 겹침 문제해결과 AppBar와 컨텐츠 내용이 제대로 상호작용하도록 하는 것이 목적이라고 한다.
자식 위젯의 크기를 측정하여 스크롤뷰의 다른 부분이 AppBar와 겹치지 않게 한다고 한다.

🪶 주요 속성

handle: SliverOverlapAbsorberHandle 위젯을 넣는다. 이는 NestedScrollView의 중첩된 부분을 이동시키는 핸들러이다.

나의 경우에는 탭 내부의 여백이 원하는대로 설정이 되지 않아(bottom 여백이 줄어들지 않음) SliverOverlapAbsorber를 사용했다.

🔩 구현

Tab을 사용하기 때문에 DefaultTabController 안에서 NestedScrollView를 호출한다. (TabBar 정리글)

🚀 코드

main_screen.dart

...

  // 홈, 일정, 기록 탭 관리용
  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(),
                ],
              ),
            ),
          ),

...

my_app_bar.dart


  Widget 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),
          ),
        ),
      ),
    );
  }

📑 Reference

Flutter 공식 문서: SliverAppBar class
Flutter 공식 문서: NestedScrollView class
Flutter 공식 문서: SliverOverlapAbsorber class
Flutter 공식 문서: SliverOverlapAbsorberHandle class

profile
😎노션 상주 중,,😎

0개의 댓글