chore(app): 추천 기록 탭 대응과 스플래시 대기 제한

- RecommendationRecordCard에 onTap 콜백을 추가하고 카드 전체를 InkWell로 감싸 탭 제스처를 받을 수 있게 함\n- _navigateToHome에서 권한 요청 Future를 5초 타임아웃으로 감싸 스플래시에서 무한 대기를 막고, 완료 여부와 관계없이 홈으로 이동하도록 정리\n- 변경 의도 주석을 추가해 동작 맥락을 명시
This commit is contained in:
JiWoong Sul
2025-12-04 16:35:27 +09:00
parent 2857fe1cb6
commit 99ad8a3bd5
2 changed files with 140 additions and 124 deletions

View File

@@ -10,11 +10,15 @@ class RecommendationRecordCard extends ConsumerWidget {
final VoidCallback onConfirmVisit; final VoidCallback onConfirmVisit;
final VoidCallback onDelete; final VoidCallback onDelete;
/// 카드 전체 탭(tap) 시 실행할 콜백.
final VoidCallback? onTap;
const RecommendationRecordCard({ const RecommendationRecordCard({
super.key, super.key,
required this.recommendation, required this.recommendation,
required this.onConfirmVisit, required this.onConfirmVisit,
required this.onDelete, required this.onDelete,
this.onTap,
}); });
String _formatTime(DateTime dateTime) { String _formatTime(DateTime dateTime) {
@@ -43,130 +47,137 @@ class RecommendationRecordCard extends ConsumerWidget {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Padding( child: InkWell(
padding: const EdgeInsets.all(16), onTap: onTap,
child: Column( borderRadius: BorderRadius.circular(12),
crossAxisAlignment: CrossAxisAlignment.start, child: Padding(
children: [ padding: const EdgeInsets.all(16),
Row( child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Container( children: [
width: 40, Row(
height: 40, children: [
decoration: BoxDecoration( Container(
color: Colors.orange.withValues(alpha: 0.1), width: 40,
shape: BoxShape.circle, height: 40,
decoration: BoxDecoration(
color: Colors.orange.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: const Icon(
Icons.whatshot,
color: Colors.orange,
size: 24,
),
), ),
child: const Icon( const SizedBox(width: 16),
Icons.whatshot, Expanded(
color: Colors.orange, child: Column(
size: 24, crossAxisAlignment: CrossAxisAlignment.start,
), children: [
), Text(
const SizedBox(width: 16), restaurant.name,
Expanded( style: AppTypography.body1(
child: Column( isDark,
crossAxisAlignment: CrossAxisAlignment.start, ).copyWith(fontWeight: FontWeight.bold),
children: [ maxLines: 1,
Text( overflow: TextOverflow.ellipsis,
restaurant.name, ),
style: AppTypography.body1( const SizedBox(height: 4),
isDark, Row(
).copyWith(fontWeight: FontWeight.bold), children: [
maxLines: 1, Icon(
overflow: TextOverflow.ellipsis, Icons.category_outlined,
), size: 14,
const SizedBox(height: 4), color: isDark
Row( ? AppColors.darkTextSecondary
children: [ : AppColors.lightTextSecondary,
Icon(
Icons.category_outlined,
size: 14,
color: isDark
? AppColors.darkTextSecondary
: AppColors.lightTextSecondary,
),
const SizedBox(width: 4),
Text(
restaurant.category,
style: AppTypography.caption(isDark),
),
const SizedBox(width: 8),
Icon(
Icons.access_time,
size: 14,
color: isDark
? AppColors.darkTextSecondary
: AppColors.lightTextSecondary,
),
const SizedBox(width: 4),
Text(
_formatTime(recommendation.recommendationDate),
style: AppTypography.caption(isDark),
),
],
),
const SizedBox(height: 8),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(
Icons.info_outline,
size: 16,
color: Colors.orange,
),
const SizedBox(width: 6),
Expanded(
child: Text(
'추천만 받은 상태입니다. 방문 후 확인을 눌러 주세요.',
style: AppTypography.caption(isDark).copyWith(
color: Colors.orange,
fontWeight: FontWeight.w600,
),
softWrap: true,
maxLines: 3,
overflow: TextOverflow.visible,
), ),
), const SizedBox(width: 4),
], Text(
), restaurant.category,
], style: AppTypography.caption(isDark),
),
const SizedBox(width: 8),
Icon(
Icons.access_time,
size: 14,
color: isDark
? AppColors.darkTextSecondary
: AppColors.lightTextSecondary,
),
const SizedBox(width: 4),
Text(
_formatTime(
recommendation.recommendationDate,
),
style: AppTypography.caption(isDark),
),
],
),
const SizedBox(height: 8),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(
Icons.info_outline,
size: 16,
color: Colors.orange,
),
const SizedBox(width: 6),
Expanded(
child: Text(
'추천만 받은 상태입니다. 방문 후 확인을 눌러 주세요.',
style: AppTypography.caption(isDark)
.copyWith(
color: Colors.orange,
fontWeight: FontWeight.w600,
),
softWrap: true,
maxLines: 3,
overflow: TextOverflow.visible,
),
),
],
),
],
),
), ),
), const SizedBox(width: 12),
const SizedBox(width: 12), ElevatedButton(
ElevatedButton( onPressed: onConfirmVisit,
onPressed: onConfirmVisit, style: ElevatedButton.styleFrom(
style: ElevatedButton.styleFrom( backgroundColor: AppColors.lightPrimary,
backgroundColor: AppColors.lightPrimary, foregroundColor: Colors.white,
foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 12),
padding: const EdgeInsets.symmetric(horizontal: 12), minimumSize: const Size(0, 40),
minimumSize: const Size(0, 40), ),
child: const Text('방문 확인'),
), ),
child: const Text('방문 확인'), ],
),
],
),
const SizedBox(height: 10),
Container(
height: 1,
color: isDark
? AppColors.darkDivider
: AppColors.lightDivider,
),
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: onDelete,
style: TextButton.styleFrom(
foregroundColor: Colors.redAccent,
padding: const EdgeInsets.only(top: 6),
minimumSize: const Size(0, 32),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: const Text('삭제'),
), ),
), const SizedBox(height: 10),
], Container(
height: 1,
color: isDark
? AppColors.darkDivider
: AppColors.lightDivider,
),
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: onDelete,
style: TextButton.styleFrom(
foregroundColor: Colors.redAccent,
padding: const EdgeInsets.only(top: 6),
minimumSize: const Size(0, 32),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: const Text('삭제'),
),
),
],
),
), ),
), ),
); );

View File

@@ -247,13 +247,18 @@ class _SplashScreenState extends State<SplashScreen>
} }
void _navigateToHome() { void _navigateToHome() {
// 권한 요청이 지연되어도 스플래시(Splash) 화면이 멈추지 않도록 최대 5초만 대기한다.
final permissionFuture = _ensurePermissions().timeout(
const Duration(seconds: 5),
onTimeout: () {},
);
Future.wait([ Future.wait([
_ensurePermissions(), permissionFuture,
Future.delayed(AppConstants.splashAnimationDuration), Future.delayed(AppConstants.splashAnimationDuration),
]).then((_) { ]).whenComplete(() {
if (mounted) { if (!mounted) return;
context.go('/home'); context.go('/home');
}
}); });
} }