import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:line_icons/line_icons.dart'; import '../../../common/theme/theme.dart'; import '../../components/appbar/appbar.dart'; import '../../components/button/button.dart'; import '../../components/spacer/spacer.dart'; import 'widgets/status_tile.dart'; import 'widgets/transaction_tile.dart'; @RoutePage() class TransactionPage extends StatefulWidget { const TransactionPage({super.key}); @override State createState() => _TransactionPageState(); } class _TransactionPageState extends State with TickerProviderStateMixin { late AnimationController _fadeController; late AnimationController _slideController; late Animation _fadeAnimation; late Animation _slideAnimation; // Filter state String selectedFilter = 'All'; final List filterOptions = [ 'All', 'Completed', 'Pending', 'Refunded', ]; @override void initState() { super.initState(); _fadeController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); _slideController = AnimationController( duration: const Duration(milliseconds: 1000), vsync: this, ); _fadeAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: _fadeController, curve: Curves.easeInOut), ); _slideAnimation = Tween(begin: const Offset(0, 0.3), end: Offset.zero).animate( CurvedAnimation(parent: _slideController, curve: Curves.elasticOut), ); _fadeController.forward(); _slideController.forward(); } @override void dispose() { _fadeController.dispose(); _slideController.dispose(); super.dispose(); } final sampleTransactions = [ Transaction( id: 'TXN001', customerName: 'John Doe', date: DateTime.now().subtract(const Duration(hours: 2)), totalAmount: 125000, itemCount: 3, paymentMethod: 'Cash', status: TransactionStatus.completed, receiptNumber: 'RCP-2024-001', ), Transaction( id: 'TXN002', customerName: 'Jane Smith', date: DateTime.now().subtract(const Duration(hours: 5)), totalAmount: 87500, itemCount: 2, paymentMethod: 'QRIS', status: TransactionStatus.pending, receiptNumber: 'RCP-2024-002', ), Transaction( id: 'TXN003', customerName: 'Bob Johnson', date: DateTime.now().subtract(const Duration(days: 1)), totalAmount: 250000, itemCount: 5, paymentMethod: 'Credit Card', status: TransactionStatus.refunded, receiptNumber: 'RCP-2024-003', ), ]; // Filter transactions based on selected status List get filteredTransactions { if (selectedFilter == 'All') { return sampleTransactions; } TransactionStatus? filterStatus; switch (selectedFilter) { case 'Completed': filterStatus = TransactionStatus.completed; break; case 'Pending': filterStatus = TransactionStatus.pending; break; case 'Refunded': filterStatus = TransactionStatus.refunded; break; } return sampleTransactions .where((transaction) => transaction.status == filterStatus) .toList(); } // Build filter chip @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColor.background, body: CustomScrollView( slivers: [ // Custom App Bar with Hero Effect SliverAppBar( expandedHeight: 120, floating: true, pinned: true, backgroundColor: AppColor.primary, centerTitle: false, flexibleSpace: CustomAppBar(title: 'Transaction', isBack: false), actions: [ ActionIconButton(onTap: () {}, icon: LineIcons.filter), SpaceWidth(8), ], ), // Pinned Filter Section SliverPersistentHeader( pinned: true, delegate: _FilterHeaderDelegate( child: Container( color: AppColor.background, padding: EdgeInsets.fromLTRB( AppValue.padding, 10, AppValue.padding, 10, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: filterOptions.map((option) { final index = filterOptions.indexOf(option); return Padding( padding: EdgeInsets.only( right: index < filterOptions.length - 1 ? 8 : 0, ), child: TransactionStatusTile( label: option, isSelected: option == selectedFilter, onSelected: (isSelected) { if (isSelected) { setState(() { selectedFilter = option; }); } }, ), ); }).toList(), ), ), ], ), ), ), ), // Content SliverPadding( padding: EdgeInsets.all(AppValue.padding), sliver: SliverList( delegate: SliverChildListDelegate([ FadeTransition( opacity: _fadeAnimation, child: SlideTransition( position: _slideAnimation, child: Column( children: [ // Show filtered transaction count if (selectedFilter != 'All') Padding( padding: const EdgeInsets.only(bottom: 16), child: Row( children: [ Text( '${filteredTransactions.length} ${selectedFilter.toLowerCase()} transaction${filteredTransactions.length != 1 ? 's' : ''}', style: TextStyle( color: AppColor.textSecondary, fontSize: 14, ), ), ], ), ), // Transaction List filteredTransactions.isEmpty ? Container( padding: const EdgeInsets.symmetric( vertical: 40, ), child: Column( children: [ Icon( LineIcons.receipt, size: 64, color: AppColor.textSecondary.withOpacity( 0.5, ), ), const SizedBox(height: 16), Text( 'No ${selectedFilter.toLowerCase()} transactions found', style: TextStyle( color: AppColor.textSecondary, fontSize: 16, ), ), ], ), ) : Column( children: filteredTransactions.map(( transaction, ) { return TransactionTile( transaction: transaction, onTap: () {}, ); }).toList(), ), ], ), ), ), ]), ), ), ], ), ); } } // Custom delegate for pinned filter header class _FilterHeaderDelegate extends SliverPersistentHeaderDelegate { final Widget child; _FilterHeaderDelegate({required this.child}); @override double get minExtent => 70; // Minimum height when collapsed @override double get maxExtent => 70; // Maximum height when expanded @override Widget build( BuildContext context, double shrinkOffset, bool overlapsContent, ) { return child; } @override bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { return false; } }