881 lines
32 KiB
Dart
881 lines
32 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:qpay/models/responsive_policy.dart';
|
|
import 'package:qpay/screens/home/home_controller.dart';
|
|
import 'package:qpay/screens/receipt/receipt_controller.dart';
|
|
import 'package:qpay/screens/transaction_controller.dart';
|
|
import 'package:qpay/screens/transactions/transaction_model.dart' as txn;
|
|
import 'package:qpay/widgets/transaction_item_widget.dart';
|
|
import 'package:skeletonizer/skeletonizer.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
class HomeScreen extends StatefulWidget {
|
|
const HomeScreen({super.key});
|
|
|
|
@override
|
|
State<HomeScreen> createState() => _HomeScreenState();
|
|
}
|
|
|
|
class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|
late HomeController homeController;
|
|
late TransactionController transactionController;
|
|
late SharedPreferences prefs;
|
|
late final router = GoRouter.of(context);
|
|
|
|
bool _isLoggedIn = false;
|
|
String initials = '';
|
|
|
|
late AnimationController _providersAnimController;
|
|
late Animation<Offset> _providersSlideAnimation;
|
|
late Animation<double> _providersFadeAnimation;
|
|
|
|
late AnimationController _transactionsAnimController;
|
|
late Animation<Offset> _transactionsSlideAnimation;
|
|
late Animation<double> _transactionsFadeAnimation;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
_providersAnimController = AnimationController(
|
|
vsync: this,
|
|
duration: const Duration(milliseconds: 600),
|
|
);
|
|
_providersSlideAnimation =
|
|
Tween<Offset>(begin: const Offset(0, 0.06), end: Offset.zero).animate(
|
|
CurvedAnimation(
|
|
parent: _providersAnimController,
|
|
curve: Curves.easeOutCubic,
|
|
),
|
|
);
|
|
_providersFadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
|
CurvedAnimation(parent: _providersAnimController, curve: Curves.easeOut),
|
|
);
|
|
|
|
_transactionsAnimController = AnimationController(
|
|
vsync: this,
|
|
duration: const Duration(milliseconds: 700),
|
|
);
|
|
_transactionsSlideAnimation =
|
|
Tween<Offset>(begin: const Offset(0, 0.06), end: Offset.zero).animate(
|
|
CurvedAnimation(
|
|
parent: _transactionsAnimController,
|
|
curve: Curves.easeOutCubic,
|
|
),
|
|
);
|
|
_transactionsFadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
|
CurvedAnimation(
|
|
parent: _transactionsAnimController,
|
|
curve: Curves.easeOut,
|
|
),
|
|
);
|
|
|
|
setupDataAndTransitions();
|
|
|
|
Future.delayed(const Duration(milliseconds: 120), () {
|
|
if (mounted) _providersAnimController.forward();
|
|
});
|
|
Future.delayed(const Duration(milliseconds: 280), () {
|
|
if (mounted) _transactionsAnimController.forward();
|
|
});
|
|
}
|
|
|
|
Future<void> setupDataAndTransitions() async {
|
|
router.routerDelegate.addListener(_onRouteChanged);
|
|
|
|
transactionController = Provider.of<TransactionController>(
|
|
context,
|
|
listen: false,
|
|
);
|
|
|
|
transactionController.model = TransactionModel();
|
|
|
|
homeController = HomeController(context);
|
|
await homeController.getAccounts();
|
|
await homeController.getCategories();
|
|
await homeController.getProviders();
|
|
|
|
prefs = await SharedPreferences.getInstance();
|
|
|
|
if (prefs.getString("token") != null) {
|
|
setState(() {
|
|
_isLoggedIn = true;
|
|
initials = prefs.getString("initials")!;
|
|
});
|
|
}
|
|
|
|
if (prefs.getString("userId") == null) {
|
|
prefs.setString("userId", Uuid().v4());
|
|
}
|
|
|
|
await homeController.getTransactions(prefs.getString("userId")!);
|
|
}
|
|
|
|
void _onRouteChanged() async {
|
|
final location = router.routerDelegate.currentConfiguration.uri.toString();
|
|
if (location.startsWith('/home')) {
|
|
prefs = await SharedPreferences.getInstance();
|
|
|
|
if (prefs.getString("userId") == null) {
|
|
prefs.setString("userId", Uuid().v4());
|
|
}
|
|
|
|
await homeController.getTransactions(prefs.getString("userId")!);
|
|
}
|
|
}
|
|
|
|
/// Handles the repeat action for a transaction.
|
|
Future<void> _repeatTransaction(Map<String, dynamic> item) async {
|
|
final receiptController = ReceiptController(context);
|
|
final transaction = txn.TransactionModel.fromJson(item);
|
|
await receiptController.repeatTransaction(transaction);
|
|
if (mounted) {
|
|
context.push('/make-payment');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_providersAnimController.dispose();
|
|
_transactionsAnimController.dispose();
|
|
homeController.dispose();
|
|
router.routerDelegate.removeListener(_onRouteChanged);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
final isDark = theme.brightness == Brightness.dark;
|
|
|
|
return LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
return Scaffold(
|
|
backgroundColor: isDark
|
|
? const Color(0xFF0D0D0D)
|
|
: const Color(0xFFF8F9FA),
|
|
body: ListenableBuilder(
|
|
listenable: homeController,
|
|
builder: (context, child) {
|
|
return CustomScrollView(
|
|
physics: const BouncingScrollPhysics(),
|
|
slivers: [
|
|
SliverAppBar(
|
|
pinned: false,
|
|
floating: true,
|
|
stretch: true,
|
|
surfaceTintColor: Colors.transparent,
|
|
backgroundColor: isDark
|
|
? const Color(0xFF0D0D0D)
|
|
: const Color(0xFFF8F9FA),
|
|
title: constraints.maxWidth < ResponsivePolicy.md
|
|
? Image(
|
|
image: const AssetImage('assets/velocity.png'),
|
|
width: 130,
|
|
)
|
|
: null,
|
|
actions: [
|
|
if (_isLoggedIn) ...[
|
|
Container(
|
|
width: 30,
|
|
height: 30,
|
|
margin: const EdgeInsets.only(right: 4),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(
|
|
color: isDark
|
|
? Colors.white.withValues(alpha: 0.12)
|
|
: Colors.black87.withAlpha(20),
|
|
),
|
|
shape: BoxShape.circle,
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
theme.colorScheme.primary.withValues(
|
|
alpha: 0.15,
|
|
),
|
|
theme.colorScheme.tertiary.withValues(
|
|
alpha: 0.05,
|
|
),
|
|
],
|
|
stops: const [0.3, 0.7],
|
|
begin: Alignment.topRight,
|
|
end: Alignment.bottomLeft,
|
|
),
|
|
),
|
|
child: Center(
|
|
child: Text(
|
|
initials,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
color: isDark ? Colors.white : Colors.black87,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: Icon(
|
|
Icons.logout_rounded,
|
|
color: isDark ? Colors.white54 : Colors.black54,
|
|
size: 20,
|
|
),
|
|
onPressed: _showLogoutDialog,
|
|
),
|
|
],
|
|
],
|
|
),
|
|
SliverToBoxAdapter(
|
|
child: Center(
|
|
child: SizedBox(
|
|
width: constraints.maxWidth > 600
|
|
? 600
|
|
: constraints.maxWidth,
|
|
child: Padding(
|
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 32),
|
|
child: LayoutBuilder(
|
|
builder: (context, innerConstraints) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
FadeTransition(
|
|
opacity: _providersFadeAnimation,
|
|
child: SlideTransition(
|
|
position: _providersSlideAnimation,
|
|
child: _buildProvidersSection(
|
|
theme,
|
|
isDark,
|
|
innerConstraints,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
FadeTransition(
|
|
opacity: _transactionsFadeAnimation,
|
|
child: SlideTransition(
|
|
position: _transactionsSlideAnimation,
|
|
child: _buildTransactionsSection(
|
|
theme,
|
|
isDark,
|
|
innerConstraints,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// Providers Section
|
|
// ──────────────────────────────────────────────────────────────
|
|
Widget _buildProvidersSection(
|
|
ThemeData theme,
|
|
bool isDark,
|
|
BoxConstraints constraints,
|
|
) {
|
|
final containerWidth = constraints.maxWidth.isFinite
|
|
? constraints.maxWidth
|
|
: 340.0;
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (!_isLoggedIn) _buildLoginPrompt(theme, isDark),
|
|
const SizedBox(height: 16),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
_buildSectionLabel(theme, isDark, Icons.grid_view_rounded, "Pay"),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
Skeletonizer(
|
|
enabled: homeController.model.isLoading,
|
|
child: Wrap(
|
|
spacing: 12,
|
|
runSpacing: 12,
|
|
children: [
|
|
...homeController.model.filterableProviders.map(
|
|
(provider) =>
|
|
_buildProviderCard(provider, theme, isDark, containerWidth),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// Provider Card — flex-based 2-column grid
|
|
// ──────────────────────────────────────────────────────────────
|
|
Widget _buildProviderCard(
|
|
BillProvider provider,
|
|
ThemeData theme,
|
|
bool isDark,
|
|
double availableWidth,
|
|
) {
|
|
final cardWidth = (availableWidth / 2) - 6;
|
|
|
|
return SizedBox(
|
|
width: cardWidth,
|
|
child: Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
borderRadius: BorderRadius.circular(16),
|
|
onTap: () {
|
|
transactionController.clearFormData();
|
|
homeController.updateSelectedProvider(provider);
|
|
transactionController.updateSelectedProvider(provider);
|
|
context.push("/recipients");
|
|
},
|
|
child: Container(
|
|
padding: const EdgeInsets.all(14),
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
theme.colorScheme.primary.withValues(alpha: 0.08),
|
|
theme.colorScheme.tertiary.withValues(alpha: 0.03),
|
|
theme.colorScheme.primary.withValues(alpha: 0.05),
|
|
],
|
|
stops: const [0.0, 0.5, 1.0],
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(
|
|
color: theme.colorScheme.primary.withValues(alpha: 0.1),
|
|
width: 1,
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: theme.colorScheme.primary.withValues(alpha: 0.06),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 3),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Container(
|
|
width: 40,
|
|
height: 40,
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: theme.colorScheme.primary.withValues(
|
|
alpha: 0.12,
|
|
),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Image(
|
|
image: AssetImage("assets/${provider.image}"),
|
|
errorBuilder: (_, __, ___) => Icon(
|
|
Icons.payment_rounded,
|
|
size: 20,
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
),
|
|
),
|
|
Icon(
|
|
Icons.chevron_right_rounded,
|
|
size: 20,
|
|
color: theme.colorScheme.primary.withValues(alpha: 0.5),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
provider.name,
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w700,
|
|
color: isDark ? Colors.white : Colors.black87,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
provider.description,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w400,
|
|
color: isDark ? Colors.white54 : Colors.grey.shade500,
|
|
),
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// Transactions Section
|
|
// ──────────────────────────────────────────────────────────────
|
|
Widget _buildTransactionsSection(
|
|
ThemeData theme,
|
|
bool isDark,
|
|
BoxConstraints constraints,
|
|
) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildSectionLabel(
|
|
theme,
|
|
isDark,
|
|
Icons.history_rounded,
|
|
"Recent Activity",
|
|
),
|
|
const SizedBox(height: 12),
|
|
if (homeController.model.transactions.isEmpty)
|
|
_buildEmptyTransactions(isDark),
|
|
if (homeController.model.transactions.isNotEmpty) ...[
|
|
...homeController.model.transactions.map(
|
|
(e) => TransactionItemWidget(
|
|
transaction: e,
|
|
enabled: homeController.model.isLoading,
|
|
onRepeat: () => _repeatTransaction(e),
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
if (homeController.model.transactions.length > 2)
|
|
Center(
|
|
child: OutlinedButton.icon(
|
|
icon: const Icon(Icons.arrow_forward_rounded, size: 16),
|
|
label: const Text('View All'),
|
|
style: OutlinedButton.styleFrom(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 20,
|
|
vertical: 10,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
side: BorderSide(
|
|
color: theme.colorScheme.primary.withValues(alpha: 0.3),
|
|
),
|
|
),
|
|
onPressed: () {
|
|
context.go('/history');
|
|
},
|
|
),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// Empty Transactions Placeholder
|
|
// ──────────────────────────────────────────────────────────────
|
|
Widget _buildEmptyTransactions(bool isDark) {
|
|
return Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 20),
|
|
decoration: BoxDecoration(
|
|
color: isDark ? const Color(0xFF1A1A2E) : Colors.white,
|
|
borderRadius: BorderRadius.circular(20),
|
|
border: Border.all(
|
|
color: isDark
|
|
? Colors.white.withValues(alpha: 0.06)
|
|
: Colors.grey.shade200,
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(
|
|
Icons.receipt_long_outlined,
|
|
size: 48,
|
|
color: isDark ? Colors.white24 : Colors.grey.shade300,
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
'No transactions yet',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: isDark ? Colors.white54 : Colors.grey.shade500,
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
'Make a payment to see your recent activity.',
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: isDark ? Colors.white38 : Colors.grey.shade400,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// Section Label
|
|
// ──────────────────────────────────────────────────────────────
|
|
Widget _buildSectionLabel(
|
|
ThemeData theme,
|
|
bool isDark,
|
|
IconData icon,
|
|
String label,
|
|
) {
|
|
return Row(
|
|
children: [
|
|
Container(
|
|
width: 32,
|
|
height: 32,
|
|
decoration: BoxDecoration(
|
|
color: theme.colorScheme.primary.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Icon(icon, size: 16, color: theme.colorScheme.primary),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Text(
|
|
label,
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w700,
|
|
color: isDark ? Colors.white : Colors.black87,
|
|
letterSpacing: 0.3,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// Login Prompt
|
|
// ──────────────────────────────────────────────────────────────
|
|
Widget _buildLoginPrompt(ThemeData theme, bool isDark) {
|
|
return Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 18),
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
theme.colorScheme.primary.withValues(alpha: 0.12),
|
|
theme.colorScheme.primary.withValues(alpha: 0.04),
|
|
],
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(
|
|
color: theme.colorScheme.primary.withValues(alpha: 0.15),
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Register or Login for more',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
color: isDark ? Colors.white : Colors.black87,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
InkWell(
|
|
onTap: () => _showFeaturesPopup(context),
|
|
borderRadius: BorderRadius.circular(4),
|
|
child: Text(
|
|
'features',
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w500,
|
|
decoration: TextDecoration.underline,
|
|
decorationColor: theme.colorScheme.primary,
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
context.push('/onboarding/landing');
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: theme.colorScheme.primary,
|
|
foregroundColor: Colors.white,
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
elevation: 0,
|
|
),
|
|
child: const Text(
|
|
'Get Started',
|
|
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// Logout Dialog
|
|
// ──────────────────────────────────────────────────────────────
|
|
void _showLogoutDialog() {
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext dialogContext) {
|
|
return AlertDialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
title: const Text('Confirm Logout'),
|
|
content: const Text('Are you sure you want to log out?'),
|
|
actions: <Widget>[
|
|
TextButton(
|
|
child: const Text('Cancel'),
|
|
onPressed: () {
|
|
Navigator.of(dialogContext).pop();
|
|
},
|
|
),
|
|
TextButton(
|
|
child: const Text('Logout'),
|
|
onPressed: () {
|
|
Navigator.of(dialogContext).pop();
|
|
prefs.clear();
|
|
setState(() {
|
|
_isLoggedIn = false;
|
|
});
|
|
setupDataAndTransitions();
|
|
},
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// Features Popup
|
|
// ──────────────────────────────────────────────────────────────
|
|
void _showFeaturesPopup(BuildContext context) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return Dialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
elevation: 0,
|
|
backgroundColor: Colors.transparent,
|
|
child: ConstrainedBox(
|
|
constraints: const BoxConstraints(maxWidth: 600),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.rectangle,
|
|
color: Theme.of(context).colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withValues(alpha: 0.1),
|
|
blurRadius: 20,
|
|
offset: const Offset(0, 10),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(
|
|
context,
|
|
).colorScheme.primary.withValues(alpha: 0.1),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Image(
|
|
image: AssetImage("assets/favicon.png"),
|
|
width: 45,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'Unlock Additional Features',
|
|
style: TextStyle(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.bold,
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Register or login to access these great features',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Theme.of(
|
|
context,
|
|
).colorScheme.onSurface.withValues(alpha: 0.7),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 24),
|
|
_buildFeatureItem(
|
|
Icons.account_balance_wallet,
|
|
'Dual Currency Wallets',
|
|
'USD and ZWG wallets for prepayments that can be utilized later',
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildFeatureItem(
|
|
Icons.history,
|
|
'Transaction History',
|
|
'All transactions are saved for reference from any device',
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildFeatureItem(
|
|
Icons.people,
|
|
'Recipient Management',
|
|
'Save and manage recipient details for future use',
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildFeatureItem(
|
|
Icons.schedule,
|
|
'Smart Payments',
|
|
'Schedule payments and split bills with friends',
|
|
),
|
|
const SizedBox(height: 24),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
style: TextButton.styleFrom(
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
child: Text(
|
|
'Maybe Later',
|
|
style: TextStyle(
|
|
color: Theme.of(
|
|
context,
|
|
).colorScheme.onSurface.withValues(alpha: 0.6),
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
context.push('/onboarding/landing');
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
backgroundColor: Theme.of(
|
|
context,
|
|
).colorScheme.primary,
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
child: const Text(
|
|
'Get Started',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildFeatureItem(IconData icon, String title, String description) {
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Icon(
|
|
icon,
|
|
size: 20,
|
|
color: Theme.of(context).colorScheme.primary,
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
description,
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Theme.of(
|
|
context,
|
|
).colorScheme.onSurface.withValues(alpha: 0.7),
|
|
height: 1.4,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|