Files
submanager/lib/widgets/detail/detail_header_section.dart
JiWoong Sul 83c5e3d64e Refactor screens to MVC architecture with modular widgets
- Extract business logic from screens into dedicated controllers
- Split large screen files into smaller, reusable widget components
- Add controllers for AddSubscriptionScreen and DetailScreen
- Create modular widgets for subscription and detail features
- Improve code organization and maintainability
- Remove duplicated code and improve reusability

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-11 00:21:18 +09:00

247 lines
9.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../models/subscription_model.dart';
import '../../controllers/detail_screen_controller.dart';
import '../website_icon.dart';
/// 상세 화면 상단 헤더 섹션
/// 서비스 아이콘, 이름, 결제 정보를 표시합니다.
class DetailHeaderSection extends StatelessWidget {
final SubscriptionModel subscription;
final DetailScreenController controller;
final Animation<double> fadeAnimation;
final Animation<Offset> slideAnimation;
final Animation<double> rotateAnimation;
const DetailHeaderSection({
super.key,
required this.subscription,
required this.controller,
required this.fadeAnimation,
required this.slideAnimation,
required this.rotateAnimation,
});
@override
Widget build(BuildContext context) {
final baseColor = controller.getCardColor();
final gradient = controller.getGradient(baseColor);
return Container(
height: 320,
decoration: BoxDecoration(gradient: gradient),
child: Stack(
children: [
// 배경 패턴
Positioned(
top: -50,
right: -50,
child: RotationTransition(
turns: rotateAnimation,
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white.withValues(alpha: 0.1),
),
),
),
),
Positioned(
bottom: -30,
left: -30,
child: Container(
width: 150,
height: 150,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white.withValues(alpha: 0.08),
),
),
),
// 콘텐츠
SafeArea(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 뒤로가기 버튼
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(
Icons.arrow_back_ios_new_rounded,
color: Colors.white,
),
onPressed: () => Navigator.of(context).pop(),
),
IconButton(
icon: const Icon(
Icons.delete_outline_rounded,
color: Colors.white,
),
onPressed: controller.deleteSubscription,
),
],
),
const Spacer(),
// 서비스 정보
FadeTransition(
opacity: fadeAnimation,
child: SlideTransition(
position: slideAnimation,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 서비스 아이콘과 이름
Row(
children: [
Hero(
tag: 'icon_${subscription.id}',
child: Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: WebsiteIcon(
url: subscription.websiteUrl,
serviceName: subscription.serviceName,
size: 48,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
subscription.serviceName,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.w800,
color: Colors.white,
letterSpacing: -0.5,
shadows: [
Shadow(
color: Colors.black26,
offset: Offset(0, 2),
blurRadius: 4,
),
],
),
),
const SizedBox(height: 4),
Text(
'${subscription.billingCycle} 결제',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white.withValues(alpha: 0.8),
),
),
],
),
),
],
),
const SizedBox(height: 20),
// 결제 정보 카드
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_InfoColumn(
label: '다음 결제일',
value: DateFormat('yyyy년 MM월 dd일')
.format(subscription.nextBillingDate),
),
_InfoColumn(
label: '월 지출',
value: NumberFormat.currency(
locale: subscription.currency == 'KRW'
? 'ko_KR'
: 'en_US',
symbol: subscription.currency == 'KRW'
? ''
: '\$',
decimalDigits:
subscription.currency == 'KRW' ? 0 : 2,
).format(subscription.monthlyCost),
alignment: CrossAxisAlignment.end,
),
],
),
),
],
),
),
),
],
),
),
),
],
),
);
}
}
/// 정보 표시 컬럼
class _InfoColumn extends StatelessWidget {
final String label;
final String value;
final CrossAxisAlignment alignment;
const _InfoColumn({
required this.label,
required this.value,
this.alignment = CrossAxisAlignment.start,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: alignment,
children: [
Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.white.withValues(alpha: 0.8),
),
),
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
],
);
}
}