412 lines
15 KiB
Dart
412 lines
15 KiB
Dart
import 'dart:ui';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
import 'package:intl/intl.dart';
|
|
import '../../models/pet_model.dart';
|
|
import '../../screens/pet_form_screen.dart';
|
|
|
|
class PetProfileCard extends StatelessWidget {
|
|
final Pet pet;
|
|
final VoidCallback? onPetUpdated;
|
|
|
|
const PetProfileCard({super.key, required this.pet, this.onPetUpdated});
|
|
|
|
// 나이 계산 (만 나이 & 사람 나이 환산 - 단순 예시)
|
|
String _calculateAge(DateTime? birthDate) {
|
|
if (birthDate == null) return '??살';
|
|
final now = DateTime.now();
|
|
int age = now.year - birthDate.year;
|
|
if (now.month < birthDate.month ||
|
|
(now.month == birthDate.month && now.day < birthDate.day)) {
|
|
age--;
|
|
}
|
|
return '$age살';
|
|
}
|
|
|
|
// 사람 나이 환산 (강아지 기준 대략적 계산 - 소형견 기준 예시)
|
|
// 사람 나이 환산 (종별 계산법 적용)
|
|
String _calculateHumanAge(DateTime? birthDate, String species) {
|
|
if (birthDate == null) return '??살';
|
|
|
|
final now = DateTime.now();
|
|
int ageYears = now.year - birthDate.year;
|
|
// 생일이 안 지났으면 만 나이 적용
|
|
if (now.month < birthDate.month ||
|
|
(now.month == birthDate.month && now.day < birthDate.day)) {
|
|
ageYears--;
|
|
}
|
|
|
|
// 개월 수 계산 (햄스터/토끼 등 단명 동물용)
|
|
int ageMonths =
|
|
(now.year - birthDate.year) * 12 + now.month - birthDate.month;
|
|
if (now.day < birthDate.day) {
|
|
ageMonths--;
|
|
}
|
|
|
|
int humanAge = 0;
|
|
|
|
switch (species) {
|
|
case '강아지':
|
|
// 강아지: 1년=15살, 2년=24살, 3년+=5살씩
|
|
if (ageYears < 1)
|
|
humanAge = (ageMonths * 1.25).round(); // 15/12
|
|
else if (ageYears == 1)
|
|
humanAge = 15;
|
|
else if (ageYears == 2)
|
|
humanAge = 24;
|
|
else
|
|
humanAge = 24 + (ageYears - 2) * 5;
|
|
break;
|
|
|
|
case '고양이':
|
|
// 고양이: 1년=15살, 2년=24살, 3년+=4살씩
|
|
if (ageYears < 1)
|
|
humanAge = (ageMonths * 1.25).round();
|
|
else if (ageYears == 1)
|
|
humanAge = 15;
|
|
else if (ageYears == 2)
|
|
humanAge = 24;
|
|
else
|
|
humanAge = 24 + (ageYears - 2) * 4;
|
|
break;
|
|
|
|
case '햄스터':
|
|
// 햄스터: 1개월=약 5살, 1년=58살, 2년=70살, その後
|
|
// 단순화: 개월당 5살로 치되, 1년차부터 보정
|
|
// 1개월~: month * 5
|
|
humanAge = ageMonths * 5;
|
|
// 1년(12개월) = 60 근사치. 2년(24개월) = 120 (너무 많음).
|
|
// 햄스터는 2~3년이 수명이므로 보정 필요
|
|
// 1년=58살, 2년=70살, 3년=100살
|
|
if (ageMonths >= 12 && ageMonths < 24) {
|
|
// 1년~2년 사이: 58 + ((month-12) * 1) -> 1년 70까지 천천히
|
|
// 58 + (ageMonths - 12); // 12개월=58, 23개월=69
|
|
humanAge = 58 + (ageMonths - 12);
|
|
} else if (ageMonths >= 24) {
|
|
// 2년 이상: 70 + (month-24)*2.5 (3년차에 100되도록)
|
|
humanAge = 70 + ((ageMonths - 24) * 2.5).round();
|
|
}
|
|
break;
|
|
|
|
case '토끼':
|
|
// 토끼: 6개월=16, 1년=21, 2년=28, 이후 +6
|
|
if (ageMonths < 6)
|
|
humanAge = (ageMonths * 2.6).round(); // 16/6
|
|
else if (ageMonths < 12)
|
|
humanAge = 16 + (ageMonths - 6); // 16~21
|
|
else if (ageYears == 1)
|
|
humanAge = 21;
|
|
else if (ageYears == 2)
|
|
humanAge = 28;
|
|
else
|
|
humanAge = 28 + (ageYears - 2) * 6;
|
|
break;
|
|
|
|
default:
|
|
// 기타 (기니피그, 앵무새, 파충류 등): 강아지 로직 fallback 혹은 1:1
|
|
// 일단 강아지 로직을 따르되, 사용자가 인지하게끔
|
|
// 여기서는 그냥 강아지와 동일하게 처리 (사용자 요청: 미적용 동물 알려달라함)
|
|
if (ageYears < 1)
|
|
humanAge = (ageMonths * 1.25).round();
|
|
else if (ageYears == 1)
|
|
humanAge = 15;
|
|
else if (ageYears == 2)
|
|
humanAge = 24;
|
|
else
|
|
humanAge = 24 + (ageYears - 2) * 5;
|
|
break;
|
|
}
|
|
|
|
return '$humanAge살';
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
margin: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
|
|
padding: EdgeInsets.all(12.w),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(20.r),
|
|
image: const DecorationImage(
|
|
image: AssetImage('assets/img/profile_card_background.png'),
|
|
fit: BoxFit.cover,
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.15),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: IntrinsicHeight(
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
// 왼쪽: 프로필 이미지
|
|
Container(
|
|
width: 120.w,
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(16.r),
|
|
color: Colors.grey[200],
|
|
image: pet.profileImageUrl != null
|
|
? DecorationImage(
|
|
image: NetworkImage(pet.profileImageUrl!),
|
|
fit: BoxFit.cover,
|
|
)
|
|
: null,
|
|
),
|
|
child: pet.profileImageUrl == null
|
|
? Center(
|
|
child: SvgPicture.asset(
|
|
'assets/icons/profile_icon.svg',
|
|
width: 40.w,
|
|
colorFilter: ColorFilter.mode(
|
|
Colors.grey[400]!,
|
|
BlendMode.srcIn,
|
|
),
|
|
),
|
|
)
|
|
: null,
|
|
),
|
|
SizedBox(width: 10.w),
|
|
// 오른쪽: 정보 영역
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Row(
|
|
children: [
|
|
Flexible(
|
|
child: Text(
|
|
pet.name,
|
|
style: TextStyle(
|
|
fontFamily: 'SCDream',
|
|
fontSize: 22.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
if (pet.gender == '남아' || pet.gender == '여아') ...[
|
|
SizedBox(width: 6.w),
|
|
Icon(
|
|
pet.gender == '남아' ? Icons.male : Icons.female,
|
|
color: pet.gender == '남아'
|
|
? Colors.blue
|
|
: Colors.pinkAccent,
|
|
size: 20.sp,
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
borderRadius: BorderRadius.circular(20),
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) =>
|
|
PetFormScreen(petToEdit: pet),
|
|
),
|
|
).then((value) {
|
|
if (value == true && onPetUpdated != null) {
|
|
onPetUpdated!();
|
|
}
|
|
});
|
|
},
|
|
child: Padding(
|
|
padding: EdgeInsets.all(8.w),
|
|
child: Icon(
|
|
Icons.arrow_forward_ios,
|
|
size: 16.sp,
|
|
color: Colors.grey[400],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 2.h),
|
|
Text(
|
|
pet.breed,
|
|
style: TextStyle(
|
|
fontFamily: 'SCDream',
|
|
fontSize: 14.sp,
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
SizedBox(height: 8.h),
|
|
// 정보 박스 (생일, 체중)
|
|
Row(
|
|
children: [
|
|
_buildInfoBox(
|
|
'생일',
|
|
pet.birthDate != null
|
|
? DateFormat('yy.MM.dd').format(pet.birthDate!)
|
|
: '??.??.??',
|
|
),
|
|
SizedBox(width: 8.w),
|
|
_buildInfoBox(
|
|
'체중',
|
|
pet.weight != null ? '${pet.weight}kg' : '--kg',
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 8.h),
|
|
// 나이 정보
|
|
FittedBox(
|
|
fit: BoxFit.scaleDown,
|
|
alignment: Alignment.centerLeft,
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
Text(
|
|
'나이 ',
|
|
style: TextStyle(
|
|
fontFamily: 'SCDream',
|
|
fontSize: 14.sp,
|
|
color: Colors.grey[600],
|
|
height: 1.2,
|
|
),
|
|
),
|
|
Text(
|
|
_calculateAge(pet.birthDate),
|
|
style: TextStyle(
|
|
fontFamily: 'SCDream',
|
|
fontSize: 15.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black,
|
|
height: 1.2,
|
|
),
|
|
),
|
|
Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 8.w),
|
|
child: Text(
|
|
'/',
|
|
style: TextStyle(
|
|
color: Colors.grey[300],
|
|
fontSize: 14.sp,
|
|
height: 1.2,
|
|
),
|
|
),
|
|
),
|
|
Text(
|
|
'사람나이 ',
|
|
style: TextStyle(
|
|
fontFamily: 'SCDream',
|
|
fontSize: 14.sp,
|
|
color: Colors.grey[600],
|
|
height: 1.2,
|
|
),
|
|
),
|
|
Text(
|
|
_calculateHumanAge(pet.birthDate, pet.species),
|
|
style: TextStyle(
|
|
fontFamily: 'SCDream',
|
|
fontSize: 15.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black,
|
|
height: 1.2,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(height: 6.h),
|
|
|
|
// 등록번호
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'등록번호 ',
|
|
style: TextStyle(
|
|
fontFamily: 'SCDream',
|
|
fontSize: 14.sp,
|
|
color: Colors.grey[600],
|
|
height: 1.2,
|
|
),
|
|
),
|
|
Expanded(
|
|
child: FittedBox(
|
|
fit: BoxFit.scaleDown,
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
pet.registrationNumber?.isNotEmpty == true
|
|
? pet.registrationNumber!
|
|
: '--',
|
|
style: TextStyle(
|
|
fontFamily: 'SCDream',
|
|
fontSize: 15.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black,
|
|
height: 1.2,
|
|
),
|
|
maxLines: 1,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildInfoBox(String label, String value) {
|
|
return Expanded(
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(12.r),
|
|
child: BackdropFilter(
|
|
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
|
child: Container(
|
|
padding: EdgeInsets.all(8.w),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.4),
|
|
border: Border.all(color: const Color(0xFFEEEEEE), width: 1),
|
|
borderRadius: BorderRadius.circular(12.r),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
label,
|
|
style: TextStyle(
|
|
fontFamily: 'SCDream',
|
|
fontSize: 12.sp,
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
SizedBox(height: 4.h),
|
|
Text(
|
|
value,
|
|
style: TextStyle(
|
|
fontFamily: 'SCDream',
|
|
fontSize: 15.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|