import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:qpay/screens/home/home_controller.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'dart:async'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late final Animation _alignAnimation; final List> _animations = []; final List> _alignListAnimations = []; late final bool _enabled = true; late bool _loading = true; Timer? _loadingTimer; bool get _isLoggedIn => true; final HomeController homeController = HomeController(); @override void initState() { super.initState(); homeController.getCategories(); _controller = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, )..forward(); _alignAnimation = Tween( begin: Alignment.centerLeft, end: Alignment.center, ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut)); 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), ), ), ); } List buttonIndices = [0, 1, 2]; 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.075 * i, 1.0, curve: Curves.easeOut), ), ), ); } // Simulate a delay to mimic loading state _loadingTimer = Timer(const Duration(seconds: 1), () { if (mounted) { setState(() { _loading = false; }); } }); } void _showErrorSnackBar(String? message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message.toString()), behavior: SnackBarBehavior.floating, ), ); } int _getCategoryIndex(String name) { return homeController.model.categories.indexWhere((e) => e.name == name); } bool _isCategoryOdd(String name) { return _getCategoryIndex(name) % 2 != 0; } @override void dispose() { _loadingTimer?.cancel(); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final VoidCallback? onPressed = _enabled ? () {} : null; return Scaffold( body: SafeArea( child: CustomScrollView( slivers: [ SliverAppBar( floating: true, title: Image(image: AssetImage('assets/icon.png'), width: 85), actions: [ IconButton( icon: const Icon(Icons.notifications_outlined), onPressed: () {}, ), ], ), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(16.0), child: 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), ], ), ), ), ListenableBuilder( listenable: homeController, builder: (context, child) { if (homeController.model.errorMessage != null) { WidgetsBinding.instance.addPostFrameCallback((_) { _showErrorSnackBar( homeController.model.errorMessage, ); }); } return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ ...homeController.model.categories.map( (e) => Skeletonizer( enabled: homeController.model.isLoading, child: SlideTransition( position: _animations[0], child: FilledButton( style: ButtonStyle( backgroundColor: WidgetStateProperty.all( _isCategoryOdd(e.name) ? Theme.of(context) .colorScheme .secondary .withValues(alpha: 0.9) : Theme.of(context) .colorScheme .primary .withValues(alpha: 0.9), ), padding: WidgetStateProperty.all( const EdgeInsets.symmetric( vertical: 17.5, horizontal: 30, ), ), ), onPressed: onPressed, child: Row( children: [ Icon( Icons.electric_bolt, color: Colors.white, ), const SizedBox(width: 8), Text( e.name, style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ], ), ), ), ), ), ], ), homeController.model.categories.isNotEmpty ? const SizedBox(height: 30) : SizedBox(), ], ); }, ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Recent Activity', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), GestureDetector( onTap: () { context.go('/history'); }, child: const Text( 'View All', style: TextStyle( fontSize: 18, fontWeight: FontWeight.normal, ), ), ), ], ), const SizedBox(height: 20), Skeletonizer( enabled: _loading, child: ListView( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: [ SlideTransition( position: _alignListAnimations[0], 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: Icon( Icons.electric_bolt, color: Theme.of(context).colorScheme.primary, ), ), title: Text( 'Electricity Bill', style: TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text( 'Paid USD 50.00', style: TextStyle(fontWeight: FontWeight.normal), ), trailing: Text( '-USD 50.00', style: TextStyle( color: Colors.red, fontWeight: FontWeight.bold, ), ), ), ), SlideTransition( position: _alignListAnimations[1], 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: Icon( Icons.phone_android, color: Theme.of(context).colorScheme.primary, ), ), title: Text( 'Airtime Purchase', style: TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text( 'Purchased USD 10.00', style: TextStyle(fontWeight: FontWeight.normal), ), trailing: Text( '-USD 10.00', style: TextStyle( color: Colors.red, fontWeight: FontWeight.bold, ), ), ), ), ], ), ), ], ), ), ), ], ), ), ); } }