1. NestedScrollView 를 사용하는 목적

위의 블로그처럼 프로필 앱 화면을 만들었다. 하지만 이 예제는 TabBarView 부분만 스크롤이 작동하고 TabBar 윗 부분은 고정되어있다.

하지만 인스타그램과 같은 앱들은 화면 전체가 스크롤화 되어있다. 화면 전체를 스크롤화하기 위해 몇가지 상황을 만들어보자.
2. Column 내부에 ListView 가 있는 경우
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}
class HomePage extends StatelessWidget {
  const HomePage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(   // 부모 컬럼
        children: [
          Container(
            height: 500,
            color: Colors.red,
          ),
          Expanded(
            child: ListView.builder(  // 자식 리스트뷰
              itemCount: 10,
              itemBuilder: (context, index) {
                return Padding(
                  padding: const EdgeInsets.only(top: 5),
                  child: Container(
                    height: 100,
                    color: Colors.blue,
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

컬럼 내부에 리스트뷰가 있다면 부모인 컬럼 박스는 그대로 유지된 채로 자식 리스트뷰만 스크롤이 생성된다.
빨간 색 리스트뷰는 작동하지만 내부의 리스트뷰는 작동되지 않는다.
스크롤을 공유하지 않는다. 이 상태에서는 위의스크롤이 끝나야 아래의 스크롤이 작동한다.
3. ListView 내부에 ListView가 있는 경우
import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}
class HomePage extends StatelessWidget {
  const HomePage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: [
          Container(
            height: 500,
            color: Colors.red,
          ),
          ListView.builder(
            itemCount: 10,
            itemBuilder: (context, index) {
              return Padding(
                padding: const EdgeInsets.only(top: 5),
                child: Container(
                  height: 100,
                  color: Colors.blue,
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}
리스트뷰 내부에 리스트뷰가 있는 경우 위와 같은 오류가 발생한다. 이 오류는 리스트뷰의 크기는 무제한이기 때문에 자식 리스트뷰가 차지할 크기를 알 수 없어 발생한 오류이다.
따라서 리스트뷰 내부에 리스트뷰가 있다면 자식 리스트뷰의 크기를 지정해주어야 한다.
import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}
class HomePage extends StatelessWidget {
  const HomePage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: [
          Container(
            height: 500,
            color: Colors.red,
          ),
          SizedBox(
            height: 1050,
            child: ListView.builder(
              itemCount: 10,
              itemBuilder: (context, index) {
                return Padding(
                  padding: const EdgeInsets.only(top: 5),
                  child: Container(
                    height: 100,
                    color: Colors.blue,
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}


이렇게 자식 리스트뷰에 SizedBox를 사용해 크기를 지정했다. 이렇게 되면 부모 리스트뷰가 있을 땐 부모 스크롤만 움직이고, 부모 리스트뷰가 없어지면 자식 리스트뷰의 스크롤이 작동한다. 빨간 부분이 화면에서 사라지면 빨간 부분을 찾기 위해 재실행을 할 수 밖에 없다.
인스타그램처럼 화면 전체의 스크롤을 공유하기 위해 NestedScrollView  를 사용한다.
4. NestedScrollView
NestedScrollView는 다른 스크롤 가능한 뷰들을 중첩하여 사용할 수 있게 해주는 위젯이다. 이는 스크롤 위치가 서로 연동되는 특징을 가지고 있어import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomePage(),
    );
  }
}
class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (context, _) {
          return [
            SliverList(
              delegate: SliverChildListDelegate([
                Container(
                  height: 500,
                  color: Colors.red,
                ),
                Container(
                  height: 500,
                  color: Colors.green,
                ),
                Container(
                  height: 500,
                  color: Colors.yellow,
                ),
              ]),
            ),
          ];
        },
        body: ListView.builder(
          physics: const NeverScrollableScrollPhysics(),
          itemCount: 10,
          itemBuilder: (context, index) {
            return Padding(
              padding: const EdgeInsets.only(top: 5),
              child: Container(
                height: 100,
                color: Colors.blue,
              ),
            );
          },
        ),
      ),
    );
  }
}


5. ProfileApp 예제NestedScrollView 로 변경하기
pages/profile_page.dart
import 'package:flutter/material.dart';
import '../components/profile_buttons.dart';
import '../components/profile_count_info.dart';
import '../components/profile_drawer.dart';
import '../components/profile_header.dart';
import '../components/profile_tab.dart';
class ProfilePage extends StatelessWidget {
  const ProfilePage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _buildAppBar(),
      endDrawer: ProfileDrawer(),
      body: NestedScrollView(
        headerSliverBuilder: (context, _) {
          return [
            SliverList(
              delegate: SliverChildListDelegate([
                ProfileHeader(),
                ProfileCountInfo(),
                ProfileButtons(),
              ]),
            ),
          ];
        },
        body: ProfileTab(),
      ),
    );
  }
  AppBar _buildAppBar() {
    return AppBar(
      leading: Icon(Icons.arrow_back_ios),
      title: Text("Profile"),
      centerTitle: true,
    );
  }
}

NestedScrollView 를 사용해 화면 전체를 스크롤이 가능하게 변경했다.
Share article
![[Flutter] Flutter 예제 - Profile App 만들기 - {CODE-RYU};](https://image.inblog.dev?url=https%3A%2F%2Finblog.ai%2Ffavicon.png&w=1920&q=75)