Flutter 앱: 달력 팝업 UI 개선
- 달력 열기 애니메이션 추가 (아래로 부드럽게 펼쳐짐) - 툴바 영역 오버레이 제외 (툴바 아래부터 반투명 배경) - 년월 텍스트 가운데 고정, 화살표는 옆에 별도 위치 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
21bd887f5e
commit
895d9c26a3
1 changed files with 74 additions and 43 deletions
|
|
@ -118,10 +118,14 @@ class _ScheduleViewState extends ConsumerState<ScheduleView> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// 달력 팝업 오버레이
|
// 달력 팝업 오버레이 (툴바 아래부터)
|
||||||
if (_showCalendar) ...[
|
if (_showCalendar) ...[
|
||||||
// 배경 오버레이
|
// 배경 오버레이 (툴바 제외)
|
||||||
Positioned.fill(
|
Positioned(
|
||||||
|
top: MediaQuery.of(context).padding.top + 56,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -134,7 +138,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 달력 팝업
|
// 달력 팝업 (애니메이션)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: MediaQuery.of(context).padding.top + 56,
|
top: MediaQuery.of(context).padding.top + 56,
|
||||||
left: 0,
|
left: 0,
|
||||||
|
|
@ -196,32 +200,43 @@ class _ScheduleViewState extends ConsumerState<ScheduleView> {
|
||||||
),
|
),
|
||||||
// 년월 표시
|
// 년월 표시
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GestureDetector(
|
child: Stack(
|
||||||
onTap: _showCalendar
|
alignment: Alignment.center,
|
||||||
? () {
|
children: [
|
||||||
setState(() {
|
// 년월 텍스트 (가운데 고정)
|
||||||
_showYearMonthPicker = !_showYearMonthPicker;
|
GestureDetector(
|
||||||
_yearRangeStart = (_calendarViewDate.year ~/ 12) * 12;
|
onTap: _showCalendar
|
||||||
});
|
? () {
|
||||||
}
|
setState(() {
|
||||||
: null,
|
_showYearMonthPicker = !_showYearMonthPicker;
|
||||||
child: Center(
|
_yearRangeStart = (_calendarViewDate.year ~/ 12) * 12;
|
||||||
child: Row(
|
});
|
||||||
mainAxisSize: MainAxisSize.min,
|
}
|
||||||
children: [
|
: null,
|
||||||
Text(
|
child: Text(
|
||||||
'${displayDate.year}년 ${displayDate.month}월',
|
'${displayDate.year}년 ${displayDate.month}월',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: _showYearMonthPicker
|
color: _showYearMonthPicker
|
||||||
? AppColors.primary
|
? AppColors.primary
|
||||||
: AppColors.textPrimary,
|
: AppColors.textPrimary,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (_showCalendar) ...[
|
),
|
||||||
const SizedBox(width: 4),
|
),
|
||||||
Icon(
|
// 화살표 (년월 옆에 위치)
|
||||||
|
if (_showCalendar)
|
||||||
|
Positioned(
|
||||||
|
// 년월 텍스트 너비의 절반 + 여백
|
||||||
|
left: MediaQuery.of(context).size.width / 2 - 48 + 52,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_showYearMonthPicker = !_showYearMonthPicker;
|
||||||
|
_yearRangeStart = (_calendarViewDate.year ~/ 12) * 12;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
_showYearMonthPicker
|
_showYearMonthPicker
|
||||||
? Icons.keyboard_arrow_up
|
? Icons.keyboard_arrow_up
|
||||||
: Icons.keyboard_arrow_down,
|
: Icons.keyboard_arrow_down,
|
||||||
|
|
@ -230,10 +245,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView> {
|
||||||
? AppColors.primary
|
? AppColors.primary
|
||||||
: AppColors.textPrimary,
|
: AppColors.textPrimary,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 다음 월
|
// 다음 월
|
||||||
|
|
@ -271,16 +285,33 @@ class _ScheduleViewState extends ConsumerState<ScheduleView> {
|
||||||
|
|
||||||
/// 달력 팝업 빌드
|
/// 달력 팝업 빌드
|
||||||
Widget _buildCalendarPopup(ScheduleState state, ScheduleController controller) {
|
Widget _buildCalendarPopup(ScheduleState state, ScheduleController controller) {
|
||||||
return Material(
|
return TweenAnimationBuilder<double>(
|
||||||
color: Colors.white,
|
duration: const Duration(milliseconds: 200),
|
||||||
elevation: 8,
|
curve: Curves.easeOut,
|
||||||
child: AnimatedCrossFade(
|
tween: Tween(begin: 0.0, end: 1.0),
|
||||||
duration: const Duration(milliseconds: 150),
|
builder: (context, value, child) {
|
||||||
crossFadeState: _showYearMonthPicker
|
return ClipRect(
|
||||||
? CrossFadeState.showSecond
|
child: Align(
|
||||||
: CrossFadeState.showFirst,
|
alignment: Alignment.topCenter,
|
||||||
firstChild: _buildCalendarGrid(state, controller),
|
heightFactor: value,
|
||||||
secondChild: _buildYearMonthPicker(),
|
child: Opacity(
|
||||||
|
opacity: value,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Material(
|
||||||
|
color: Colors.white,
|
||||||
|
elevation: 8,
|
||||||
|
child: AnimatedCrossFade(
|
||||||
|
duration: const Duration(milliseconds: 150),
|
||||||
|
crossFadeState: _showYearMonthPicker
|
||||||
|
? CrossFadeState.showSecond
|
||||||
|
: CrossFadeState.showFirst,
|
||||||
|
firstChild: _buildCalendarGrid(state, controller),
|
||||||
|
secondChild: _buildYearMonthPicker(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue