Files
velocity-pay-flutter/lib/screens/confirm/confirm_screen.dart

435 lines
20 KiB
Dart

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:qpay/models/responsive_policy.dart';
import 'package:qpay/screens/confirm/confirm_controller.dart';
import 'package:skeletonizer/skeletonizer.dart';
class ConfirmScreen extends StatefulWidget {
const ConfirmScreen({super.key});
@override
State<ConfirmScreen> createState() => _ConfirmScreenState();
}
class _ConfirmScreenState extends State<ConfirmScreen>
with TickerProviderStateMixin {
late ConfirmController controller;
late Animation<double> _fadeAnimation;
late Animation<Offset> _slideAnimation;
late AnimationController _controller;
final _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
controller = ConfirmController(context);
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 800),
);
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
),
);
_slideAnimation = Tween<Offset>(
begin: const Offset(0, 0.1),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
),
);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading:
context.canPop()
? IconButton(
onPressed: () {
context.pop();
},
icon: const Icon(Icons.arrow_back),
)
: SizedBox.shrink(),
title: const Text(
'Confirm Payment',
style: TextStyle(color: Colors.black87),
),
),
body: ListenableBuilder(
listenable: controller,
builder: (context, child) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: FadeTransition(
opacity: _fadeAnimation,
child: SlideTransition(
position: _slideAnimation,
child: Form(
key: _formKey,
child: Center(
child: LayoutBuilder(
builder: (context, constraints) {
return SizedBox(
width: double.parse(constraints.maxWidth > ResponsivePolicy.md ?
ResponsivePolicy.md.toString() :
constraints.maxWidth.toString()),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.3),
),
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color:
Theme.of(context).colorScheme.primary,
),
),
),
padding: EdgeInsets.only(
bottom: 4.0,
), // Adjust spacing
child: Text(
"PROVIDER DETAILS",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
...controller
.transactionController
.model
.confirmationData["additionalData"]
.map<Widget>((data) {
return Column(
children: [
const SizedBox(height: 10),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
data["name"] ?? "",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
data["value"] ?? "",
style: TextStyle(fontSize: 16),
),
],
),
],
);
})
.toList(),
const SizedBox(height: 30),
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color:
Theme.of(context).colorScheme.primary,
),
),
),
padding: EdgeInsets.only(
bottom: 4.0,
), // Adjust spacing
child: Text(
"TRANSACTIONS DETAILS",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Amount",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
controller
.transactionController
.model
.confirmationData["amount"]
.toString(),
style: TextStyle(fontSize: 16),
),
],
),
const SizedBox(height: 10),
if (controller
.transactionController
.model
.confirmationData["charge"] !=
0)
Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Our Charge",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
controller
.transactionController
.model
.confirmationData["charge"]
.toString(),
style: TextStyle(fontSize: 16),
),
],
),
const SizedBox(height: 10),
],
),
if (controller
.transactionController
.model
.confirmationData["gatewayCharge"] !=
0)
Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Gateway Charge",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
controller
.transactionController
.model
.confirmationData["gatewayCharge"]
.toString(),
style: TextStyle(fontSize: 16),
),
],
),
const SizedBox(height: 10),
],
),
if (controller
.transactionController
.model
.confirmationData["tax"] !=
0)
Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Tax",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
controller
.transactionController
.model
.confirmationData["tax"]
.toString(),
style: TextStyle(fontSize: 16),
),
],
),
const SizedBox(height: 10),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Total Amount",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
controller
.transactionController
.model
.confirmationData["totalAmount"]
.toString(),
style: TextStyle(fontSize: 16),
),
],
),
const SizedBox(height: 5),
Text(
"NB: Your payment provider may charge additional fees.",
style: TextStyle(
fontSize: 11,
color: Colors.red,
),
),
],
),
),
const SizedBox(height: 20),
_buildPaymentProcessorButton(),
const SizedBox(height: 20),
Center(
child: OutlinedButton(
child: Text('Cancel'),
onPressed: () {
context.go('/');
},
),
),
],
),
);
}
),
),
),
),
),
),
);
},
),
);
}
Widget _buildPaymentProcessorButton() {
return Skeletonizer(
enabled: controller.model.isLoading,
child: SlideTransition(
position: _slideAnimation,
child: Container(
decoration: BoxDecoration(
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,
),
),
child: InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
onTap: () async {
if (controller.model.isLoading) return;
await controller.doTransaction();
},
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/${controller.transactionController.model.selectedPaymentProcessor.image}",
),
),
),
title: Text(
controller
.transactionController
.model
.selectedPaymentProcessor
.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
controller
.transactionController
.model
.selectedPaymentProcessor
.description,
style: TextStyle(fontWeight: FontWeight.normal, fontSize: 10),
),
trailing: Icon(
Icons.chevron_right,
color: Theme.of(
context,
).colorScheme.secondary.withValues(alpha: 0.7),
),
),
),
),
),
);
}
}