import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:timeago/timeago.dart' as timeago; import 'package:qpay/screens/home/home_controller.dart'; import 'package:qpay/screens/transaction_controller.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'dart:async'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:uuid/uuid.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _alignAnimation; late TransactionController transactionController; late bool _loading = true; late SharedPreferences prefs; late HomeController homeController; Timer? _loadingTimer; bool get _isLoggedIn => false; String _selectedFilter = 'All'; final List> _animations = []; final List> _alignListAnimations = []; @override void initState() { super.initState(); transactionController = Provider.of( context, listen: false, ); // Reset the transaction controller model transactionController.model = TransactionModel(); _controller = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, )..forward(); List tranListIndices = [0, 1]; for (int i = 0; i < tranListIndices.length; i++) { _alignListAnimations.add( Tween(begin: Offset(0, -0.25), end: Offset.zero).animate( CurvedAnimation( parent: _controller, curve: Interval(0.075 * i, 1.0, curve: Curves.easeOut), ), ), ); } _alignAnimation = Tween( begin: Alignment.centerLeft, end: Alignment.center, ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut)); setupDataAndTransitions(); // Simulate a delay to mimic loading state _loadingTimer = Timer(const Duration(seconds: 1), () { if (mounted) { setState(() { _loading = false; }); } }); } Future setupDataAndTransitions() async { homeController = HomeController(context); await homeController.getCategories(); await homeController.getProviders(); List buttonIndices = List.generate( homeController.model.providers.length, (index) => index, ); for (int i = 0; i < buttonIndices.length; i++) { _animations.add( Tween(begin: Offset(-0.15, 0), end: Offset.zero).animate( CurvedAnimation( parent: _controller, curve: Interval(0.055 * i, 1.0, curve: Curves.easeOut), ), ), ); } prefs = await SharedPreferences.getInstance(); if (prefs.getString("userId") == null) { prefs.setString("userId", Uuid().v4()); } await homeController.getTransactions(prefs.getString("userId")!); } getBoxDecoration(String? clientId) { // BillProvider? provider = homeController.model.selectedProvider; // // if (provider != null && clientId != provider.clientId) { // return BoxDecoration( // border: Border.all(color: Colors.black87.withAlpha(20)), // borderRadius: BorderRadius.circular(12), // ); // } return BoxDecoration( // border: Border.all( // color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.7), // width: 3, // ), border: Border.all(color: Colors.black87.withAlpha(20)), borderRadius: BorderRadius.circular(12), gradient: LinearGradient( colors: [ Theme.of(context).colorScheme.primary.withValues(alpha: 0.1), Theme.of(context).colorScheme.tertiary.withValues(alpha: 0.05), ], stops: const [0.3, 0.7], begin: Alignment.topRight, end: Alignment.bottomLeft, ), ); } @override void dispose() { _loadingTimer?.cancel(); _controller.dispose(); homeController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: RefreshIndicator( onRefresh: () async { await homeController.getTransactions(prefs.getString("userId")!); }, child: CustomScrollView( slivers: [ SliverAppBar( stretch: true, stretchTriggerOffset: 100.0, expandedHeight: 50.0, surfaceTintColor: Colors.transparent, floating: true, title: Image(image: AssetImage('assets/peak.png'), width: 85), actions: [ // IconButton( // icon: const Icon(Icons.notifications_outlined), // onPressed: () {}, // ), ], ), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(20.0), child: ListenableBuilder( listenable: homeController, builder: (context, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (_isLoggedIn) AlignTransition( alignment: _alignAnimation, child: Skeletonizer( enabled: _loading, child: Column( children: [ Text( "Vusumuzi Khoza", style: TextStyle(fontSize: 15), ), Text( "USD 4.02", style: TextStyle( fontSize: 30, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 20), ], ), ), ), _buildFilterChips(homeController), SizedBox(height: 10), Column( children: [ ...homeController.model.filterableProviders.map( (e) => _buildProviderItem(e, context), ), ], ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ const Text( 'Recent Activity', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 5), if (homeController.model.transactions.isEmpty) Column( children: [ SizedBox(height: 30), Center( child: Text( 'No transactions yet. Make a payment to see your recent activity.', style: TextStyle(fontSize: 16), textAlign: TextAlign.center, ), ), ], ), if (homeController.model.transactions.isNotEmpty) Column( children: [ ListView( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: [ ...homeController.model.transactions.map( (e) => _buildTransactionItem(e, context), ), ], ), SizedBox(height: 10), if (homeController.model.transactions.length > 2) OutlinedButton( child: Text('View All'), onPressed: () { context.go('/history'); }, ), ], ), ], ); }, ), ), ), ], ), ), ), ); } Widget _buildTransactionItem(Map e, BuildContext context) { return SlideTransition( position: _alignListAnimations[0], child: Skeletonizer( enabled: homeController.model.isLoading, child: InkWell( customBorder: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), onTap: () { transactionController.updateReceiptData(e); context.push("/receipt"); }, child: ListTile( leading: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Theme.of( context, ).colorScheme.primary.withValues(alpha: 0.1), shape: BoxShape.circle, ), child: Image( image: AssetImage("assets/${e['paymentProcessorImage']}"), ), ), title: Row( children: [ Text( e['billName'], style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(width: 10), Icon( e['status'] == 'SUCCESS' ? Icons.check_circle : e['status'] == 'PENDING' ? Icons.pending : Icons.cancel, size: 16, color: e['status'] == 'SUCCESS' ? Colors.green : e['status'] == 'PENDING' ? Colors.orange : Colors.red, ), ], ), subtitle: Text( e['createdAt'] != null ? timeago.format(DateTime.parse(e['createdAt'])) : '', style: TextStyle(fontSize: 12), ), trailing: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( e['amount'].toStringAsFixed(2), style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), ), Text( e['totalAmount'].toStringAsFixed(2), style: TextStyle(fontSize: 12), ), ], ), ), ), ), ); } Column _buildProviderItem(BillProvider e, BuildContext context) { return Column( children: [ Skeletonizer( enabled: homeController.model.isLoading, child: SlideTransition( position: _animations[homeController.model.providers.indexOf(e)], child: Container( decoration: getBoxDecoration(e.clientId), child: InkWell( customBorder: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), onTap: () { homeController.updateSelectedProvider(e); transactionController.updateSelectedProvider(e); context.push("/recipients"); }, child: ListTile( leading: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Theme.of( context, ).colorScheme.primary.withValues(alpha: 0.1), shape: BoxShape.circle, ), child: Image(image: AssetImage("assets/${e.image}")), ), title: Text( e.name, style: TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text( e.description, style: TextStyle( fontWeight: FontWeight.normal, fontSize: 10, ), ), trailing: Icon( Icons.chevron_right, color: Theme.of( context, ).colorScheme.secondary.withValues(alpha: 0.7), ), ), ), ), ), ), SizedBox(height: 10), ], ); } Widget _buildFilterChips(HomeController controller) { return SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: [ _buildFilterChip('All', controller), ...controller.model.categories.map((category) { return _buildFilterChip(category.name, controller, category); }), ], ), ); } Widget _buildFilterChip( String label, HomeController controller, [ Category? category, ]) { final isSelected = _selectedFilter == label; return Skeletonizer( enabled: controller.model.isLoading, child: Padding( padding: const EdgeInsets.only(right: 8), child: FilterChip( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: Theme.of( context, ).colorScheme.primary.withValues(alpha: 0.2), ), ), selected: isSelected, label: Text(label), onSelected: (selected) { setState(() { _selectedFilter = label; }); if (category != null) { controller.updateSelectedCategory(category); } else { controller.updateSelectedCategory(null); } }, backgroundColor: Theme.of(context).colorScheme.surface, selectedColor: Theme.of(context).colorScheme.primary.withOpacity(0.2), checkmarkColor: Theme.of(context).colorScheme.primary, labelStyle: TextStyle( color: isSelected ? Theme.of( context, ).colorScheme.secondary.withValues(alpha: 0.7) : Theme.of(context).colorScheme.onSurface, ), ), ), ); } }