Files
velocity-pay-flutter/lib/screens/home_screen.dart
2025-06-10 22:17:38 +02:00

339 lines
9.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _slideAnimation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 250),
);
_slideAnimation = Tween<double>(begin: -1.0, end: 0.0).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeOutCubic),
);
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: CustomScrollView(
slivers: [
SliverAppBar(
floating: true,
title: const Text('QPay'),
actions: [
IconButton(
icon: const Icon(Icons.notifications_outlined),
onPressed: () {},
),
],
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Welcome back,',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
const SizedBox(height: 4),
const Text(
'John Doe',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 24),
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0),
end: const Offset(0, 1),
).animate(_slideAnimation),
child: _buildBalanceCard(context),
),
const SizedBox(height: 24),
const Text(
'Quick Actions',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
_buildQuickActions(context),
const SizedBox(height: 24),
const Text(
'Recent Transactions',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
_buildRecentTransactions(),
],
),
),
),
],
),
),
);
}
Widget _buildBalanceCard(BuildContext context) {
return Card(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Available Balance',
style: TextStyle(color: Colors.black87, fontSize: 14),
),
const SizedBox(height: 4),
const Text(
'\$0.00',
style: TextStyle(
color: Colors.black,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),
_buildActionButton(
context,
'Add',
Icons.add_circle_outline,
() {},
),
],
),
],
),
),
);
}
Widget _buildActionButton(
BuildContext context,
String label,
IconData icon,
VoidCallback onTap,
) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.black87.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(icon, color: Colors.white, size: 18),
const SizedBox(width: 4),
Text(
label,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 14,
),
),
],
),
),
);
}
Widget _buildQuickActions(BuildContext context) {
return GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: 2,
children: [
_buildAnimatedQuickActionItem(
context,
'Electricity',
Icons.electric_bolt_outlined,
() {},
0,
),
_buildAnimatedQuickActionItem(
context,
'Airtime',
Icons.phone_android_outlined,
() {},
1,
),
],
);
}
Widget _buildAnimatedQuickActionItem(
BuildContext context,
String label,
IconData icon,
VoidCallback onTap,
int index,
) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: const Offset(0, 0),
).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(0.4 + (index * 0.1), 0.8 + (index * 0.1)),
),
),
child: _buildQuickActionItem(context, label, icon, onTap),
);
}
Widget _buildQuickActionItem(
BuildContext context,
String label,
IconData icon,
VoidCallback onTap,
) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(
icon,
color: Theme.of(context).colorScheme.secondary,
size: 28,
),
),
const SizedBox(height: 8),
Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.tertiary,
),
),
],
),
);
}
Widget _buildRecentTransactions() {
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 3,
itemBuilder: (context, index) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(
0.5 + (index * 0.1),
0.7 + (index * 0.1),
curve: Curves.easeOutCubic,
),
),
),
child: ListTile(
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.tertiary.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
index == 0
? Icons.phone_android
: index == 1
? Icons.wifi
: Icons.receipt_long,
color: Theme.of(context).colorScheme.tertiary,
),
),
title: Text(
index == 0
? 'Mobile Bill'
: index == 1
? 'Internet Bill'
: 'Electricity Bill',
),
subtitle: Text(
index == 0
? 'Yesterday'
: index == 1
? '2 days ago'
: '3 days ago',
style: const TextStyle(fontSize: 12),
),
trailing: Text(
index == 0
? '-\$50.00'
: index == 1
? '-\$75.00'
: '-\$120.00',
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontWeight: FontWeight.bold,
),
),
),
);
},
);
}
}