import 'package:flutter/material.dart'; import '../models/subscription_model.dart'; import '../widgets/subscription_card.dart'; import '../widgets/category_header_widget.dart'; /// 카테고리별로 구독 목록을 표시하는 위젯 class SubscriptionListWidget extends StatelessWidget { final Map> categorizedSubscriptions; final AnimationController fadeController; const SubscriptionListWidget({ Key? key, required this.categorizedSubscriptions, required this.fadeController, }) : super(key: key); @override Widget build(BuildContext context) { // 카테고리 키 목록 (정렬된) final categories = categorizedSubscriptions.keys.toList(); return SliverList( delegate: SliverChildBuilderDelegate( (context, index) { final category = categories[index]; final subscriptions = categorizedSubscriptions[category]!; return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 카테고리 헤더 Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 8), child: CategoryHeaderWidget( categoryName: category, subscriptionCount: subscriptions.length, totalCost: subscriptions.fold( 0.0, (sum, sub) => sum + sub.monthlyCost, ), ), ), // 카테고리별 구독 목록 FadeTransition( opacity: Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: fadeController, curve: Curves.easeIn)), child: ListView.builder( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: subscriptions.length, itemBuilder: (context, subIndex) { // 각 구독의 지연값 계산 (순차적으로 나타나도록) final delay = 0.05 * subIndex; final animationBegin = 0.2; final animationEnd = 1.0; final intervalStart = delay; final intervalEnd = intervalStart + 0.4; // 간격 계산 (0.0~1.0 사이의 값으로 정규화) final intervalStartNormalized = intervalStart.clamp(0.0, 0.9); final intervalEndNormalized = intervalEnd.clamp(0.1, 1.0); return FadeTransition( opacity: Tween( begin: animationBegin, end: animationEnd) .animate(CurvedAnimation( parent: fadeController, curve: Interval(intervalStartNormalized, intervalEndNormalized, curve: Curves.easeOut))), child: Padding( padding: const EdgeInsets.only(bottom: 12.0), child: SubscriptionCard( subscription: subscriptions[subIndex], ), ), ); }, ), ), ], ), ); }, childCount: categories.length, ), ); } } /// 여러 Sliver 위젯을 하나의 위젯으로 감싸는 도우미 위젯 class MultiSliver extends StatelessWidget { final List children; const MultiSliver({ Key? key, required this.children, }) : super(key: key); @override Widget build(BuildContext context) { return SliverList( delegate: SliverChildBuilderDelegate( (context, index) { if (index >= children.length) return null; return children[index]; }, childCount: children.length, ), ); } }