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