작성
·
25
0
안녕하세요.
현재 코드입니다.
import 'package:flutter/material.dart';
import 'dart:developer' as developer;
class TestSearchScreen extends StatefulWidget {
const TestSearchScreen({Key? key}) : super(key: key);
@override
_TestSearchScreenState createState() => _TestSearchScreenState();
}
class _TestSearchScreenState extends State<TestSearchScreen>
with SingleTickerProviderStateMixin {
late TabController _tabController;
late ScrollController _scrollController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
_scrollController = ScrollController()..addListener(_scrollListener);
WidgetsBinding.instance.addPostFrameCallback((_) {
_logScreenInfo();
});
}
@override
void dispose() {
_scrollController.removeListener(_scrollListener);
_scrollController.dispose();
_tabController.dispose();
super.dispose();
}
void _scrollListener() {
developer.log('현재 스크롤 위치: ${_scrollController.offset}', name: '스크롤 디버그');
developer.log('최대 스크롤 범위: ${_scrollController.position.maxScrollExtent}',
name: '스크롤 디버그');
developer.log('최소 스크롤 범위: ${_scrollController.position.minScrollExtent}',
name: '스크롤 디버그');
developer.log(
'현재 스크롤 방향: ${_scrollController.position.userScrollDirection}',
name: '스크롤 디버그');
developer.log('스크롤 위치가 범위 내에 있음: ${_scrollController.position.outOfRange}',
name: '스크롤 디버그');
developer.log('현재 뷰포트 크기: ${_scrollController.position.viewportDimension}',
name: '스크롤 디버그');
}
void _logScreenInfo() {
final mediaQuery = MediaQuery.of(context);
developer.log('화면 정보:', name: '스크롤 디버그');
developer.log(' 화면 크기: ${mediaQuery.size}', name: '스크롤 디버그');
developer.log(' 화면 너비: ${mediaQuery.size.width}', name: '스크롤 디버그');
developer.log(' 화면 높이: ${mediaQuery.size.height}', name: '스크롤 디버그');
developer.log(' 상태 표시줄 높이: ${mediaQuery.padding.top}', name: '스크롤 디버그');
developer.log(' 바닥 패딩: ${mediaQuery.padding.bottom}', name: '스크롤 디버그');
developer.log(
' SafeArea 높이: ${mediaQuery.size.height - mediaQuery.padding.top - mediaQuery.padding.bottom}',
name: '스크롤 디버그');
}
@override
Widget build(BuildContext context) {
developer.log('TestSearchScreen 빌드 호출됨', name: '스크롤 디버그');
return Scaffold(
body: SafeArea(
child: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
// 헤더 부분에 들어갈 슬리버 위젯들
return <Widget>[
SliverAppBar(
title: const Text('Test Search'),
floating: true,
snap: true,
forceElevated: innerBoxIsScrolled,
),
SliverToBoxAdapter(
child: _buildProfileInfo(),
),
SliverPersistentHeader(
pinned: true,
delegate: _TabBarDelegate(
TabBar(
controller: _tabController,
onTap: (index) {
developer.log('탭 변경됨: $index', name: '스크롤 디버그');
},
tabs: const [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
),
),
SliverToBoxAdapter(
child: _buildFilter(),
),
];
},
// 본문 부분에는 TabBarView 배치
body: TabBarView(
controller: _tabController,
children: [
_buildTab(1, Colors.red),
_buildTab(5, Colors.green),
_buildTab(1, Colors.blue),
],
),
),
),
);
}
Widget _buildProfileInfo() {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('User Profile',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Container(height: 100, color: Colors.grey[300]),
const SizedBox(height: 16),
],
),
);
}
Widget _buildFilter() {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {}, child: const Text('Filter 1'))),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton(
onPressed: () {}, child: const Text('Filter 2'))),
],
),
);
}
// 탭 컨텐츠를 위한 새로운 메서드
Widget _buildTab(int count, Color color) {
return Builder(builder: (BuildContext context) {
// NestedScrollView의 내부 스크롤 동작을 위해 Builder 사용
return GridView.builder(
padding: const EdgeInsets.all(16),
// NestedScrollView 내부의 스크롤에 맞는 physics 사용
physics: const BouncingScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: count,
itemBuilder: (context, index) {
developer.log('그리드 아이템 빌드: index=$index', name: '스크롤 디버그');
return Container(
color: color,
child: Center(child: Text('아이템 $index')),
);
},
);
});
}
}
class _TabBarDelegate extends SliverPersistentHeaderDelegate {
final TabBar tabBar;
_TabBarDelegate(this.tabBar);
@override
double get minExtent => tabBar.preferredSize.height;
@override
double get maxExtent => tabBar.preferredSize.height;
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: tabBar,
);
}
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return false;
}
}
Threads 앱의 프로필 화면처럼 구현하려고 합니다.
현재 구현은
• 스크롤 내릴 때는 탭바만 상단에 고정되고 있고
• 스크롤 올릴 때는 앱바, 프로필 정보, 탭바가 원래 위치로 복귀되고 있습니다.
문제는
탭바 아래 컨텐츠가 적을 때 발생하는 문제:
스크롤 할 컨텐츠 양이 부족해도 스크롤이 불필요하게 탭바 고정 위치까지 이동하고 있습니다.
컨텐츠가 부족할 경우, 탭바 고정 위치까지 스크롤되지 않고, 컨텐츠 높이까지만 스크롤되도록 하고 싶은데 어떻게해야될까요?
(스레드앱처럼구현되길원합니다)
(스레드앱사진인데 포스트가없으면 스크롤이 되지 않습니다.)
고수분들 의견 부탁드립니다.
답변