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