Files
velocity-pay-flutter/lib/screens/recipient/recipients_screen.dart

406 lines
16 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:qpay/models/responsive_policy.dart';
import 'package:qpay/screens/transaction_controller.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:qpay/screens/recipient/recipients_controller.dart';
import 'package:shared_preferences/shared_preferences.dart';
class RecipientsScreen extends StatefulWidget {
const RecipientsScreen({super.key});
@override
State<RecipientsScreen> createState() => _RecipientsScreenState();
}
class _RecipientsScreenState extends State<RecipientsScreen>
with TickerProviderStateMixin {
late SharedPreferences prefs;
late AnimationController _animationController;
late RecipientsController controller;
late TransactionController transactionController;
final List<Animation<Offset>> _alignListAnimations = [];
final _accountController = TextEditingController();
final _queryParams = <String, String>{};
@override
void initState() {
super.initState();
controller = RecipientsController(context);
setupData();
controller.model.creditAccount = _accountController.text;
transactionController = Provider.of<TransactionController>(
context,
listen: false,
);
_animationController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
)..forward();
List tranListIndices = [0, 1];
for (int i = 0; i < tranListIndices.length; i++) {
_alignListAnimations.add(
Tween<Offset>(begin: Offset(0, -0.25), end: Offset.zero).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(0.075 * i, 1.0, curve: Curves.easeOut),
),
),
);
}
}
Future<void> setupData() async {
prefs = await SharedPreferences.getInstance();
_queryParams['userId'] = prefs.getString("userId")!;
controller.getRecipients(_queryParams);
}
@override
void dispose() {
_animationController.dispose();
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Recipients", style: TextStyle(color: Colors.black87)),
),
body: ListenableBuilder(
listenable: controller,
builder: (context, child) {
return SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20),
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(
decoration: BoxDecoration(
border: Border.all(color: Colors.black87.withAlpha(20)),
borderRadius: BorderRadius.circular(12),
),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"TRANSACTIONS DETAILS",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Provider",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
Text(
controller.model.selectedProvider?.name ?? "",
style: TextStyle(fontSize: 16),
),
],
),
],
),
),
const SizedBox(height: 20),
_buildAccountField(controller),
const SizedBox(height: 20),
controller.model.creditAccount != null &&
controller.model.creditAccount!.isNotEmpty
? SlideTransition(
position: _alignListAnimations[0],
child: ElevatedButton(
onPressed: () {
String phone = '';
if (!transactionController
.model
.selectedProvider!
.requiresAccount) {
phone = _accountController.text;
}
transactionController.model.formData =
transactionController.model.formData.copyWith(
creditAccount: _accountController.text,
creditPhone: phone,
);
context.push("/make-payment");
},
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).colorScheme.primary,
foregroundColor:
Theme.of(context).colorScheme.onPrimary,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Use as new recipient",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 5),
Icon(Icons.arrow_forward_ios, size: 12),
],
),
),
)
: SizedBox(),
const SizedBox(height: 20),
Text(
"Recent Recipients",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
_buildRecipientItems(controller),
],
),
);
}
),
),
),
);
},
),
);
}
Widget _buildAccountField(RecipientsController controller) {
String fieldName =
controller.model.selectedProvider?.accountFieldName ?? "Account Number";
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(fieldName, style: TextStyle(fontSize: 16)),
TextButton(
onPressed: () async {
if (await FlutterContacts.requestPermission()) {
final contact = await FlutterContacts.openExternalPick();
if (contact != null) {
_accountController.text = contact.phones.first.number;
controller.updateCreditAccount(contact.phones.first.number);
}
}
},
child: Text(
"Fetch from contacts",
style: TextStyle(fontSize: 12, color: Colors.red),
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: TextFormField(
controller: _accountController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Enter / Search $fieldName',
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Theme.of(context).colorScheme.secondary,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.2),
),
),
),
onChanged: (value) {
controller.updateCreditAccount(value);
},
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an amount';
}
if (double.tryParse(value) == null) {
return 'Please enter a valid amount';
}
return null;
},
),
),
IconButton(
onPressed: () {
_queryParams.clear();
setState(() {
_queryParams['account'] = _accountController.text;
_queryParams['email'] = _accountController.text;
_queryParams['phoneNumber'] = _accountController.text;
_queryParams['name'] = _accountController.text;
_queryParams['userId'] = prefs.getString("userId")!;
controller.getRecipients(_queryParams);
});
},
icon: Icon(
Icons.search,
color: Theme.of(context).colorScheme.primary,
size: 24,
),
),
],
),
],
);
}
Widget _buildRecipientItems(RecipientsController controller) {
if (controller.model.recipients.isEmpty && !controller.model.isLoading) {
return Center(child: Text("No recipients yet"));
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...controller.model.recipients.map(
(recipient) => Skeletonizer(
enabled: controller.model.isLoading,
child: InkWell(
onTap: () {
String account = recipient.account ?? '';
if (!transactionController
.model
.selectedProvider!
.requiresAccount) {
account = recipient.phoneNumber ?? '';
}
transactionController
.model
.formData = transactionController.model.formData.copyWith(
creditAccount: account,
creditName: recipient.name ?? '',
creditPhone: recipient.phoneNumber ?? '',
creditEmail: recipient.email ?? '',
providerLabel:
transactionController.model.formData.providerLabel.isEmpty
? recipient.latestProviderLabel ?? ''
: transactionController.model.formData.providerLabel,
);
context.push("/make-payment");
},
borderRadius: BorderRadius.circular(12),
child: Container(
padding: EdgeInsets.all(5),
child: SlideTransition(
position: _alignListAnimations[0],
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(
context,
).colorScheme.primary.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Text(
recipient.initials ?? 'PK',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(width: 20),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
recipient.latestProviderLabel ?? '',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(width: 5),
Text(
recipient.account ?? '',
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
if ((recipient.phoneNumber != null &&
recipient.phoneNumber!.isNotEmpty) ||
(recipient.email != null &&
recipient.email!.isNotEmpty))
Row(
spacing: 5,
children: [
if (recipient.phoneNumber != null &&
recipient.phoneNumber!.isNotEmpty)
Text(
recipient.phoneNumber ?? '',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
if (recipient.email != null &&
recipient.email!.isNotEmpty)
Text(
recipient.email ?? '',
style: TextStyle(
fontWeight: FontWeight.normal,
),
),
],
),
if (recipient.name != null &&
recipient.name!.isNotEmpty)
Text(
recipient.name ?? '',
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
],
),
),
),
),
),
),
],
);
}
}