상점탭 삭제 / 데일리케어 ui

This commit is contained in:
youngbeom 2026-01-25 18:23:24 +09:00
parent 721b748703
commit 4059ea9af7
10 changed files with 744 additions and 45 deletions

View File

@ -1,12 +1,11 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1_12643)">
<circle cx="10" cy="10" r="10" fill="#FF7500"/>
<path d="M11.0585 15.1849H8.92441C6.91251 15.1849 5.90629 15.1849 5.28154 14.5596C4.65678 13.9343 4.65625 12.9286 4.65625 10.9167V9.84969C4.65625 7.83779 4.65625 6.83157 5.28154 6.20682C5.90682 5.58206 6.91251 5.58153 8.92441 5.58153H11.0585C13.0704 5.58153 14.0766 5.58153 14.7014 6.20682C15.3261 6.8321 15.3267 7.83779 15.3267 9.84969V10.9167C15.3267 12.9286 15.3267 13.9349 14.7014 14.5596C14.353 14.9085 13.8862 15.0627 13.1926 15.1305M7.32385 5.58153V4.78125M12.6591 5.58153V4.78125M15.0599 8.24913H9.32455M4.65625 8.24913H6.72364" stroke="white" stroke-width="1.3" stroke-linecap="round"/>
<path d="M13.1913 12.5114C13.1913 12.6529 13.1351 12.7886 13.035 12.8886C12.935 12.9887 12.7993 13.0449 12.6578 13.0449C12.5163 13.0449 12.3806 12.9887 12.2805 12.8886C12.1805 12.7886 12.1243 12.6529 12.1243 12.5114C12.1243 12.3699 12.1805 12.2341 12.2805 12.1341C12.3806 12.034 12.5163 11.9778 12.6578 11.9778C12.7993 11.9778 12.935 12.034 13.035 12.1341C13.1351 12.2341 13.1913 12.3699 13.1913 12.5114ZM13.1913 10.3773C13.1913 10.5188 13.1351 10.6545 13.035 10.7545C12.935 10.8546 12.7993 10.9108 12.6578 10.9108C12.5163 10.9108 12.3806 10.8546 12.2805 10.7545C12.1805 10.6545 12.1243 10.5188 12.1243 10.3773C12.1243 10.2358 12.1805 10.1001 12.2805 10C12.3806 9.89996 12.5163 9.84375 12.6578 9.84375C12.7993 9.84375 12.935 9.89996 13.035 10C13.1351 10.1001 13.1913 10.2358 13.1913 10.3773ZM10.5237 12.5114C10.5237 12.6529 10.4675 12.7886 10.3674 12.8886C10.2674 12.9887 10.1317 13.0449 9.99018 13.0449C9.84869 13.0449 9.71298 12.9887 9.61293 12.8886C9.51287 12.7886 9.45666 12.6529 9.45666 12.5114C9.45666 12.3699 9.51287 12.2341 9.61293 12.1341C9.71298 12.034 9.84869 11.9778 9.99018 11.9778C10.1317 11.9778 10.2674 12.034 10.3674 12.1341C10.4675 12.2341 10.5237 12.3699 10.5237 12.5114ZM10.5237 10.3773C10.5237 10.5188 10.4675 10.6545 10.3674 10.7545C10.2674 10.8546 10.1317 10.9108 9.99018 10.9108C9.84869 10.9108 9.71298 10.8546 9.61293 10.7545C9.51287 10.6545 9.45666 10.5188 9.45666 10.3773C9.45666 10.2358 9.51287 10.1001 9.61293 10C9.71298 9.89996 9.84869 9.84375 9.99018 9.84375C10.1317 9.84375 10.2674 9.89996 10.3674 10C10.4675 10.1001 10.5237 10.2358 10.5237 10.3773ZM7.8561 12.5114C7.8561 12.6529 7.79989 12.7886 7.69984 12.8886C7.59978 12.9887 7.46408 13.0449 7.32258 13.0449C7.18108 13.0449 7.04538 12.9887 6.94533 12.8886C6.84527 12.7886 6.78906 12.6529 6.78906 12.5114C6.78906 12.3699 6.84527 12.2341 6.94533 12.1341C7.04538 12.034 7.18108 11.9778 7.32258 11.9778C7.46408 11.9778 7.59978 12.034 7.69984 12.1341C7.79989 12.2341 7.8561 12.3699 7.8561 12.5114ZM7.8561 10.3773C7.8561 10.5188 7.79989 10.6545 7.69984 10.7545C7.59978 10.8546 7.46408 10.9108 7.32258 10.9108C7.18108 10.9108 7.04538 10.8546 6.94533 10.7545C6.84527 10.6545 6.78906 10.5188 6.78906 10.3773C6.78906 10.2358 6.84527 10.1001 6.94533 10C7.04538 9.89996 7.18108 9.84375 7.32258 9.84375C7.46408 9.84375 7.59978 9.89996 7.69984 10C7.79989 10.1001 7.8561 10.2358 7.8561 10.3773Z" fill="white"/>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_77_604)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5 3.75376C13.5 4.15158 13.342 4.53311 13.0607 4.81442C12.7794 5.09572 12.3978 5.25376 12 5.25376C11.6022 5.25376 11.2206 5.09572 10.9393 4.81442C10.658 4.53311 10.5 4.15158 10.5 3.75376C10.5 3.35593 10.658 2.9744 10.9393 2.6931C11.2206 2.41179 11.6022 2.25376 12 2.25376C12.3978 2.25376 12.7794 2.41179 13.0607 2.6931C13.342 2.9744 13.5 3.35593 13.5 3.75376ZM15.675 3.00376C15.5029 2.15604 15.043 1.3939 14.3732 0.846476C13.7034 0.299049 12.865 0 12 0C11.135 0 10.2966 0.299049 9.62681 0.846476C8.95705 1.3939 8.49714 2.15604 8.325 3.00376H4.5C4.10218 3.00376 3.72064 3.16179 3.43934 3.4431C3.15804 3.7244 3 4.10593 3 4.50376V22.5038C3 22.9016 3.15804 23.2831 3.43934 23.5644C3.72064 23.8457 4.10218 24.0038 4.5 24.0038H19.5C19.8978 24.0038 20.2794 23.8457 20.5607 23.5644C20.842 23.2831 21 22.9016 21 22.5038V4.50376C21 4.10593 20.842 3.7244 20.5607 3.4431C20.2794 3.16179 19.8978 3.00376 19.5 3.00376H15.675ZM12 7.50376H8.25V5.25376H5.25V21.7538H18.75V5.25376H15.75V7.50376H12Z" fill="#C8C8C8"/>
<path d="M8.47461 10.5283C8.2757 10.5283 8.08493 10.6073 7.94428 10.748C7.80363 10.8886 7.72461 11.0794 7.72461 11.2783C7.72461 11.4772 7.80363 11.668 7.94428 11.8087C8.08493 11.9493 8.2757 12.0283 8.47461 12.0283H15.5297C15.7286 12.0283 15.9194 11.9493 16.0601 11.8087C16.2007 11.668 16.2797 11.4772 16.2797 11.2783C16.2797 11.0794 16.2007 10.8886 16.0601 10.748C15.9194 10.6073 15.7286 10.5283 15.5297 10.5283H8.47461ZM8.47461 13.5283C8.2757 13.5283 8.08493 13.6073 7.94428 13.748C7.80363 13.8886 7.72461 14.0794 7.72461 14.2783C7.72461 14.4772 7.80363 14.668 7.94428 14.8087C8.08493 14.9493 8.2757 15.0283 8.47461 15.0283H15.5297C15.7286 15.0283 15.9194 14.9493 16.0601 14.8087C16.2007 14.668 16.2797 14.4772 16.2797 14.2783C16.2797 14.0794 16.2007 13.8886 16.0601 13.748C15.9194 13.6073 15.7286 13.5283 15.5297 13.5283H8.47461ZM7.72461 17.2783C7.72461 17.0794 7.80363 16.8886 7.94428 16.748C8.08493 16.6073 8.2757 16.5283 8.47461 16.5283H14.0297C14.2286 16.5283 14.4194 16.6073 14.5601 16.748C14.7007 16.8886 14.7797 17.0794 14.7797 17.2783C14.7797 17.4772 14.7007 17.668 14.5601 17.8087C14.4194 17.9493 14.2286 18.0283 14.0297 18.0283H8.47461C8.2757 18.0283 8.08493 17.9493 7.94428 17.8087C7.80363 17.668 7.72461 17.4772 7.72461 17.2783Z" fill="#C8C8C8"/>
</g>
<defs>
<clipPath id="clip0_1_12643">
<rect width="20" height="20" fill="white"/>
<clipPath id="clip0_77_604">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,10 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_80_2119)">
<path d="M7.71875 10.4297L3.28906 6L7.71875 1.57031L7.71875 10.4297Z" fill="#1F1F1F" stroke="#1F1F1F" stroke-width="2.95312" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_80_2119">
<rect width="12" height="12" fill="white" transform="translate(12 12) rotate(-180)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 438 B

View File

@ -0,0 +1,10 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_80_2122)">
<path d="M4.28906 1.57031L8.71875 6L4.28906 10.4297V1.57031Z" fill="#1F1F1F" stroke="#1F1F1F" stroke-width="2.95312" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_80_2122">
<rect width="12" height="12" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 388 B

View File

@ -2,6 +2,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:intl/date_symbol_data_local.dart'; // Import here
import 'dart:developer';
import 'screens/splash_screen.dart';
import 'screens/register_complete_screen.dart';
@ -11,6 +12,7 @@ final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting('ko_KR', null); // Add this line
//
FlutterError.onError = (FlutterErrorDetails details) {

View File

@ -0,0 +1,607 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:intl/intl.dart';
import '../models/pet_model.dart';
import '../services/firestore_service.dart';
import '../theme/app_colors.dart';
class DailyCareScreen extends StatefulWidget {
const DailyCareScreen({super.key});
@override
State<DailyCareScreen> createState() => _DailyCareScreenState();
}
class _DailyCareScreenState extends State<DailyCareScreen> {
final FirestoreService _firestoreService = FirestoreService();
DateTime _focusedDay = DateTime.now();
DateTime _selectedDay = DateTime.now();
String? _userId;
Pet? _selectedPet;
final DraggableScrollableController _sheetController =
DraggableScrollableController();
@override
void dispose() {
_sheetController.dispose();
super.dispose();
}
DateTime _normalizeDate(DateTime date) {
return DateTime(date.year, date.month, date.day);
}
late final Map<DateTime, List<String>> _events;
@override
void initState() {
super.initState();
_userId = _firestoreService.getCurrentUserId();
final today = _normalizeDate(DateTime.now());
_events = {
today: [
'flower',
'flower',
'flower',
'flower',
'incomplete',
'flower',
'important',
'general',
'general',
], // : 9 (3x3 )
today.subtract(const Duration(days: 1)): [
'flower',
'flower',
'flower',
], // : 3
today.subtract(const Duration(days: 2)): ['general'], // 2: 1
today.subtract(const Duration(days: 3)): [
'incomplete',
'flower',
], // 3: 1, 1
today.subtract(const Duration(days: 5)): [
'flower',
'flower',
'flower',
], // 5: 3
today.subtract(const Duration(days: 7)): [
'incomplete',
'incomplete',
], // 7: 2
today.subtract(const Duration(days: 10)): [
'important',
'general',
], // 10: 1, 1
};
}
List<String> _getEventsForDay(DateTime day) {
return _events[_normalizeDate(day)] ?? [];
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Stack(
children: [
Column(
children: [
_buildHeader(),
SizedBox(height: 30.h), // Equal spacing 1
_buildCustomCalendarHeader(), // Custom Header
SizedBox(height: 30.h), // Equal spacing 2
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: _buildCalendar(),
),
),
],
),
_buildBottomSheet(),
],
),
),
floatingActionButton: FloatingActionButton(
heroTag: 'daily_care_fab',
onPressed: () {
// ( )
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('일정 추가 기능은 준비 중입니다.')));
},
backgroundColor: AppColors.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
child: const Icon(Icons.add, color: Colors.white),
),
);
}
Widget _buildHeader() {
return Padding(
padding: EdgeInsets.only(left: 20.w, right: 20.w, top: 10.h, bottom: 0),
child: StreamBuilder<List<Pet>>(
stream: _userId != null
? _firestoreService.getPets(_userId!)
: const Stream.empty(),
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const SizedBox.shrink();
}
final pets = snapshot.data!;
if (_selectedPet == null ||
!pets.any((p) => p.id == _selectedPet!.id)) {
_selectedPet = pets.first;
}
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
InkWell(
onTap: () {},
child: Row(
children: [
CircleAvatar(
radius: 20.r,
backgroundColor: Colors.grey[200],
backgroundImage: _selectedPet!.profileImageUrl != null
? NetworkImage(_selectedPet!.profileImageUrl!)
: null,
child: _selectedPet!.profileImageUrl == null
? SvgPicture.asset(
'assets/icons/profile_icon.svg',
width: 20.w,
colorFilter: ColorFilter.mode(
Colors.grey[400]!,
BlendMode.srcIn,
),
)
: null,
),
SizedBox(width: 8.w),
Text(
_selectedPet!.name,
style: TextStyle(
fontFamily: 'SCDream',
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
Icon(Icons.keyboard_arrow_down, size: 24.w),
],
),
),
],
);
},
),
);
}
Widget _buildCustomCalendarHeader() {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: 20.w,
), // Removed vertical padding
child: Stack(
alignment: Alignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
setState(() {
_focusedDay = DateTime(
_focusedDay.year,
_focusedDay.month - 1,
);
});
},
child: SvgPicture.asset(
'assets/icons/left_icon.svg',
width: 12.w, // Changed to 12px
height: 12.w,
),
),
SizedBox(width: 30.w),
Text(
DateFormat('yyyy년 M월', 'ko_KR').format(_focusedDay),
style: TextStyle(
fontFamily: 'SCDream',
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 30.w),
InkWell(
onTap: () {
setState(() {
_focusedDay = DateTime(
_focusedDay.year,
_focusedDay.month + 1,
);
});
},
child: SvgPicture.asset(
'assets/icons/right_icon.svg',
width: 12.w, // Changed to 12px
height: 12.w,
),
),
],
),
Positioned(
right: 0,
child: Container(
width: 40.w,
height: 40.w,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(12.r),
),
child: const Icon(Icons.download, color: Colors.white, size: 20),
),
),
],
),
);
}
Widget _buildCalendar() {
return TableCalendar(
shouldFillViewport: false,
locale: 'ko_KR',
rowHeight: 85
.h, // Increased to fit 3 rows of icons (approx 40px + 32px top offset)
daysOfWeekHeight: 30.h,
firstDay: DateTime.utc(2020, 1, 1),
lastDay: DateTime.utc(2030, 12, 31),
focusedDay: _focusedDay,
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
headerVisible: false, // Hide default header
onDaySelected: (selectedDay, focusedDay) {
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
});
},
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
},
calendarStyle: CalendarStyle(
defaultTextStyle: TextStyle(
fontFamily: 'SCDream',
fontWeight: FontWeight.w500,
fontSize: 15.sp,
color: const Color(0xFF1f1f1f),
),
weekendTextStyle: TextStyle(
fontFamily: 'SCDream',
fontWeight: FontWeight.w500,
fontSize: 15.sp,
color: const Color(0xFF1f1f1f),
),
todayDecoration: const BoxDecoration(
color: Colors.transparent,
shape: BoxShape.circle,
),
todayTextStyle: const TextStyle(
color: AppColors.highlight,
fontWeight: FontWeight.bold,
),
selectedDecoration: const BoxDecoration(
color: Colors.transparent,
shape: BoxShape.circle,
),
selectedTextStyle: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
),
outsideDaysVisible: true, // Show outside days
),
eventLoader: _getEventsForDay, //
calendarBuilders: CalendarBuilders(
//
markerBuilder: (context, day, events) {
if (events.isEmpty) return null;
// ( )
// : _focusedDay는 ,
// day의 .
// markerBuilder는 .
bool isOutside = day.month != _focusedDay.month;
double opacity = isOutside ? 0.5 : 1.0;
// ( 9 )
return Positioned(
top: 32.h,
left: 0,
right: 0,
child: Opacity(
opacity: opacity,
child: Center(
child: Container(
width:
40.w, // Exact width: 12*3 + 2*2 = 40. Centers perfectly.
child: Wrap(
alignment: WrapAlignment.start, // Left aligned
spacing: 2.w, // Horizontal spacing
runSpacing: 2.h, // Vertical spacing
children: events.take(9).map((event) {
String iconPath =
'assets/icons/general_schedule_icon.svg';
switch (event) {
case 'flower':
iconPath = 'assets/icons/flower_icon.svg';
break;
case 'incomplete':
iconPath = 'assets/icons/incomplete_icon.svg';
break;
case 'important':
iconPath = 'assets/icons/important_schedule_icon.svg';
break;
case 'general':
iconPath = 'assets/icons/general_schedule_icon.svg';
break;
}
return SvgPicture.asset(
iconPath,
width: 12.w,
height: 12.w,
);
}).toList(),
),
),
),
),
);
},
//
dowBuilder: (context, day) {
final text = DateFormat.E('ko_KR').format(day);
// '현재 달' ( )
Color color = const Color(0xFF939393);
if (day.weekday == DateTime.sunday)
color = const Color(0xFFFF3F3F); // #FF3F3F로
return Container(
// Removed bottom border to avoid double border with first row's top border
padding: EdgeInsets.only(bottom: 10.h),
alignment: Alignment.bottomCenter,
child: Text(
text,
style: TextStyle(
color: color,
fontFamily: 'SCDream',
fontSize: 12.sp,
fontWeight: FontWeight.w500,
),
),
);
},
// (/)
outsideBuilder: (context, day, focusedDay) {
// 50%
Color color = const Color(0xFF1F1F1F).withOpacity(0.5); // 50%
if (day.weekday == DateTime.sunday) {
color = const Color(0xFFFF3F3F).withOpacity(0.5); // 50%
}
return Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: Color(0xFFE0E0E0),
width: 1,
), // Changed to top
),
),
padding: EdgeInsets.only(top: 8.h),
alignment: Alignment.topCenter,
child: Text(
day.day.toString().padLeft(2, '0'),
style: TextStyle(
fontFamily: 'SCDream',
fontSize: 15.sp,
fontWeight: FontWeight.w500,
color: color,
),
),
);
},
//
defaultBuilder: (context, day, focusedDay) {
//
Color color = const Color(0xFF1F1F1F);
if (day.weekday == DateTime.sunday) {
color = const Color(0xFFFF3F3F);
}
return Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: Color(0xFFE0E0E0),
width: 1,
), // Changed to top
),
),
padding: EdgeInsets.only(top: 8.h), // Move closer to top line
alignment: Alignment.topCenter, // Align to top
child: Text(
day.day.toString().padLeft(2, '0'),
style: TextStyle(
fontFamily: 'SCDream',
fontSize: 15.sp,
fontWeight: FontWeight.w500,
color: color,
),
),
);
},
// /
prioritizedBuilder: (context, day, focusedDay) {
final isToday = isSameDay(day, DateTime.now());
final isSelected = isSameDay(day, _selectedDay);
// null을 default/outsideBuilder가
if (!isToday && !isSelected) return null;
final isSunday = day.weekday == DateTime.sunday;
//
Color textColor = const Color(0xFF1F1F1F); //
if (isToday) {
textColor = const Color(0xFFFF9500); // : #FF9500 ()
} else if (isSunday) {
textColor = const Color(0xFFFF3F3F); // : #FF3F3F
}
// / ( )
BoxDecoration? innerDecoration;
if (isSelected) {
innerDecoration = BoxDecoration(
color: const Color(0xFFFFEDBC), // : #FFEDBC
borderRadius: BorderRadius.circular(6.r), // (6)
);
}
return Container(
// :
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFFE0E0E0), width: 1),
),
),
child: Container(
// : ( )
margin: EdgeInsets.symmetric(vertical: 2.h),
decoration: innerDecoration,
alignment: Alignment.topCenter, //
padding: EdgeInsets.only(top: 6.h), //
child: Text(
day.day.toString().padLeft(2, '0'),
style: TextStyle(
fontFamily: 'SCDream',
fontSize: 15.sp,
fontWeight: FontWeight.bold, // /
decoration: isSelected ? null : null,
color: textColor,
),
),
),
);
},
),
);
}
Widget _buildBottomSheet() {
return DraggableScrollableSheet(
controller: _sheetController, //
initialChildSize: 0.08,
minChildSize: 0.08,
maxChildSize: 0.8,
builder: (context, scrollController) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 20.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(24.r)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05), // Lighter shadow
blurRadius: 5, // Tighter blur
offset: const Offset(0, -3), // Closer offset
),
],
),
child: SingleChildScrollView(
controller: scrollController,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
child: Column(
children: [
// ( )
GestureDetector(
onTap: () {
//
if (_sheetController.size > 0.4) {
_sheetController.animateTo(
0.08,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
} else {
_sheetController.animateTo(
0.8,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
},
child: Container(
color: Colors.transparent, //
width: double.infinity,
child: Column(
children: [
AnimatedBuilder(
animation: _sheetController,
builder: (context, child) {
// 0.08 ~ 0.8
// 0.4 ,
// 0.4
final double angle =
_sheetController.isAttached &&
_sheetController.size > 0.4
? 3.14159
: 0.0;
return Transform.rotate(
angle: angle,
child: Icon(
Icons.keyboard_arrow_up_rounded,
color: Colors.grey[400],
size: 24.w,
),
);
},
),
SizedBox(height: 20.h),
],
),
),
),
Align(
alignment: Alignment.centerLeft,
child: Text(
'중요 일정 1',
style: TextStyle(
fontFamily: 'SCDream',
fontSize: 14.sp,
color: Colors.grey[600],
),
),
),
SizedBox(height: 50.h), //
],
),
),
),
);
},
);
}
}

View File

@ -2,9 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; // Import screenutil
import 'home_screen.dart';
import 'reservation_screen.dart';
import 'daily_care_screen.dart';
import 'mungnyangz_screen.dart';
import 'shop_screen.dart';
import 'my_info_screen.dart';
import '../theme/app_colors.dart';
@ -22,9 +21,8 @@ class _MainScreenState extends State<MainScreen> {
//
final List<Widget> _screens = [
const HomeScreen(),
const ReservationScreen(),
const MungNyangzScreen(),
const ShopScreen(),
const DailyCareScreen(),
const MungNyangzScreen(), // ( )
const MyInfoScreen(),
];
@ -94,9 +92,9 @@ class _MainScreenState extends State<MainScreen> {
BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.only(bottom: 10.h),
child: _buildSvgIcon('assets/icons/appointment_icon.svg', 1),
child: _buildSvgIcon('assets/icons/calendar_icon.svg', 1),
),
label: '예약/조회',
label: '데일리케어',
),
BottomNavigationBarItem(
icon: Padding(
@ -115,14 +113,10 @@ class _MainScreenState extends State<MainScreen> {
BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.only(bottom: 10.h),
child: _buildSvgIcon('assets/icons/shop_icon.svg', 3),
),
label: '상점',
),
BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.only(bottom: 10.h),
child: _buildSvgIcon('assets/icons/my_icon.svg', 4),
child: _buildSvgIcon(
'assets/icons/my_icon.svg',
3,
), // Index adjusted
),
label: '내 정보',
),

View File

@ -6,8 +6,6 @@ import 'package:image_picker/image_picker.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../theme/app_colors.dart';
import '../data/pet_data.dart';
import '../widgets/pet_registration/selection_modal.dart';
import '../widgets/pet_registration/input_formatters.dart';
import '../services/firestore_service.dart';
import '../models/pet_model.dart';
@ -1724,3 +1722,86 @@ class _PetFormScreenState extends State<PetFormScreen> {
);
}
}
// --- Helper Classes (Restored) ---
class DateRangeInputFormatter extends TextInputFormatter {
final int min;
final int max;
DateRangeInputFormatter({required this.min, required this.max});
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
if (newValue.text.isEmpty) {
return newValue;
}
final int? value = int.tryParse(newValue.text);
if (value == null) {
return oldValue;
}
if (value < min || value > max) {
return oldValue;
}
return newValue;
}
}
class DayInputFormatter extends TextInputFormatter {
final TextEditingController monthController;
final TextEditingController yearController;
DayInputFormatter({
required this.monthController,
required this.yearController,
});
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
if (newValue.text.isEmpty) {
return newValue;
}
final int? day = int.tryParse(newValue.text);
if (day == null) {
return oldValue;
}
int? month = int.tryParse(monthController.text);
int? year = int.tryParse(yearController.text);
if (month == null || month < 1 || month > 12) {
// 31
if (day < 1 || day > 31) return oldValue;
return newValue;
}
int maxDay = _getDaysInMonth(year, month);
if (day < 1 || day > maxDay) {
return oldValue;
}
return newValue;
}
int _getDaysInMonth(int? year, int month) {
if (month == 2) {
if (year != null &&
((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) {
return 29;
}
return 28;
}
const daysInMonth = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
return daysInMonth[month];
}
}

View File

@ -1,21 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; // Import screenutil
class ShopScreen extends StatelessWidget {
const ShopScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Center(
child: Text(
'상점 화면 준비 중입니다.',
style: TextStyle(fontFamily: 'SCDream', fontSize: 14.sp),
),
),
),
);
}
}

View File

@ -720,6 +720,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
simple_gesture_detector:
dependency: transitive
description:
name: simple_gesture_detector
sha256: ba2cd5af24ff20a0b8d609cec3f40e5b0744d2a71804a2616ae086b9c19d19a3
url: "https://pub.dev"
source: hosted
version: "0.2.1"
sky_engine:
dependency: transitive
description: flutter
@ -757,6 +765,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
table_calendar:
dependency: "direct main"
description:
name: table_calendar
sha256: b2896b7c86adf3a4d9c911d860120fe3dbe03c85db43b22fd61f14ee78cdbb63
url: "https://pub.dev"
source: hosted
version: "3.1.3"
term_glyph:
dependency: transitive
description:

View File

@ -46,6 +46,7 @@ dependencies:
cloud_firestore: ^5.0.0
firebase_storage: ^12.0.0
uuid: ^4.0.0
table_calendar: ^3.0.9
dev_dependencies:
flutter_test: