diff --git a/lib/features/dashboard/screens/dashboard_screen.dart b/lib/features/dashboard/screens/dashboard_screen.dart index f85ddce..1462fbc 100644 --- a/lib/features/dashboard/screens/dashboard_screen.dart +++ b/lib/features/dashboard/screens/dashboard_screen.dart @@ -5,6 +5,7 @@ import 'package:kmobile/features/cheque/screens/cheque_management_screen.dart'; import 'package:kmobile/features/customer_info/screens/customer_info_screen.dart'; import 'package:kmobile/features/beneficiaries/screens/manage_beneficiaries_screen.dart'; import 'package:kmobile/features/enquiry/screens/enquiry_screen.dart'; +import 'package:kmobile/features/quick_pay/screens/quick_pay_screen.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; class DashboardScreen extends StatefulWidget { @@ -113,7 +114,10 @@ class _DashboardScreenState extends State { builder: (context) => const CustomerInfoScreen())); }), _buildQuickLink(Symbols.currency_rupee, "Quick \n Pay", - () => print("")), + () { + Navigator.push(context, MaterialPageRoute( + builder: (context) => const QuickPayScreen())); + }), _buildQuickLink(Symbols.send_money, "Fund \n Transfer", () => print("")), _buildQuickLink(Symbols.server_person, "Account \n Info", diff --git a/lib/features/quick_pay/screens/quick_pay_outside_bank_screen.dart b/lib/features/quick_pay/screens/quick_pay_outside_bank_screen.dart new file mode 100644 index 0000000..82cb132 --- /dev/null +++ b/lib/features/quick_pay/screens/quick_pay_outside_bank_screen.dart @@ -0,0 +1,368 @@ +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +class QuickPayOutsideBankScreen extends StatefulWidget { + const QuickPayOutsideBankScreen({super.key}); + + @override + State createState() => + _QuickPayOutsideBankScreen(); +} + +class _QuickPayOutsideBankScreen extends State { + final _formKey = GlobalKey(); + + // Controllers + final accountNumberController = TextEditingController(); + final nameController = TextEditingController(); + final bankNameController = TextEditingController(); + final branchNameController = TextEditingController(); + final ifscController = TextEditingController(); + final phoneController = TextEditingController(); + final amountController = TextEditingController(); + + String accountType = 'Savings'; + final List transactionModes = ['NEFT', 'RTGS', 'IMPS']; + int selectedTransactionIndex = 0; + + @override + void dispose() { + // Dispose controllers + accountNumberController.dispose(); + nameController.dispose(); + bankNameController.dispose(); + branchNameController.dispose(); + ifscController.dispose(); + phoneController.dispose(); + amountController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Symbols.arrow_back_ios_new), + onPressed: () { + Navigator.pop(context); + }, + ), + title: const Text( + 'Quick Pay - Other Bank', + style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500), + ), + centerTitle: false, + actions: const [ + Padding( + padding: EdgeInsets.only(right: 10.0), + child: CircleAvatar( + backgroundImage: AssetImage('assets/images/avatar.jpg'), + // Replace with your image + radius: 20, + ), + ), + ], + ), + body: Padding( + padding: const EdgeInsets.all(12), + child: Form( + key: _formKey, + child: ListView( + children: [ + const SizedBox(height: 25), + TextFormField( + decoration: const InputDecoration( + labelText: 'Account Number', + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + controller: accountNumberController, + keyboardType: TextInputType.number, + textInputAction: TextInputAction.next, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Account number is required'; + } else if (value.length != 16) { + return 'Enter a valid account number'; + } + return null; + }, + ), + const SizedBox(height: 25), + TextFormField( + decoration: const InputDecoration( + labelText: 'Name', + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + controller: nameController, + keyboardType: TextInputType.name, + textInputAction: TextInputAction.next, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Name is required'; + } + return null; + }, + ), + const SizedBox(height: 25), + TextFormField( + decoration: const InputDecoration( + labelText: "Beneficiary Bank Name", + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + controller: bankNameController, + textInputAction: TextInputAction.next, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Beneficiary Bank Name is required'; + } + return null; + }, + ), + const SizedBox(height: 25), + TextFormField( + decoration: const InputDecoration( + labelText: "Branch Name", + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + controller: branchNameController, + textInputAction: TextInputAction.next, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Branch Name is required'; + } + return null; + }, + ), + const SizedBox(height: 25), + Row( + children: [ + Expanded( + child: TextFormField( + decoration: const InputDecoration( + labelText: "IFSC Code", + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + controller: ifscController, + textInputAction: TextInputAction.next, + validator: (value) { + if (value == null || value.isEmpty) { + return 'IFSC Code is required'; + } + return null; + }, + )), + const SizedBox(width: 10), + Expanded( + child: DropdownButtonFormField( + value: accountType, + decoration: const InputDecoration( + labelText: "Account Type", + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + items: ['Savings', 'Current'] + .map((e) => DropdownMenuItem( + value: e, + child: Text(e), + )) + .toList(), + onChanged: (value) => setState(() { + accountType = value!; + }), + ), + ), + ], + ), + const SizedBox(height: 25), + Row( + children: [ + Expanded( + child: TextFormField( + controller: phoneController, + keyboardType: TextInputType.phone, + decoration: const InputDecoration( + labelText: "Phone", + prefixIcon: Icon(Icons.phone), + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + textInputAction: TextInputAction.next, + validator: (value) => value == null || value.isEmpty + ? "Phone number is Required" + : null, + ), + ), + const SizedBox(width: 10), + Expanded( + child: TextFormField( + decoration: const InputDecoration( + labelText: 'Amount', + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + controller: amountController, + keyboardType: TextInputType.number, + textInputAction: TextInputAction.next, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Amount is required'; + } + final amount = double.tryParse(value); + if (amount == null || amount <= 0) { + return 'Enter a valid amount'; + } + return null; + }, + ), + ), + ], + ), + const SizedBox(height: 30), + Row( + children: [ + const Text("Transaction Mode", + style: TextStyle(fontWeight: FontWeight.w500)), + const SizedBox(width: 12), + Expanded(child: buildTransactionModeSelector()), + ], + ), + + const SizedBox(height: 45), + Align( + alignment: Alignment.center, + child: SizedBox( + width: 250, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: const StadiumBorder(), + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: Colors.blue[900], + foregroundColor: Colors.white, + ), + onPressed: () { + if (_formKey.currentState!.validate()) { + // Perform payment logic + final selectedMode = transactionModes[selectedTransactionIndex]; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Paying via $selectedMode...')), + ); + } + }, + child: const Text('Pay'), + ), + ), + ), + ], + ), + ), + ), + ); + } + + Widget buildTransactionModeSelector() { + return Container( + padding: const EdgeInsets.all(4), + child: Row( + children: List.generate(transactionModes.length, (index) { + bool isSelected = selectedTransactionIndex == index; + return Expanded( + child: GestureDetector( + onTap: () { + setState(() => selectedTransactionIndex = index); + }, + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.symmetric(vertical: 5), + decoration: BoxDecoration( + color: isSelected ? Colors.blue[200] : Colors.white, + borderRadius: BorderRadius.circular(5), + border: Border.all( + color: isSelected? Colors.blue : Colors.grey, + width: isSelected ? 0 : 1.2, + ), + ), + alignment: Alignment.center, + child: Text( + transactionModes[index], + style: const TextStyle( + color: Colors.black, + ), + ), + ), + ), + ); + }), + ), + ); + } +} diff --git a/lib/features/quick_pay/screens/quick_pay_screen.dart b/lib/features/quick_pay/screens/quick_pay_screen.dart new file mode 100644 index 0000000..a1a0bf6 --- /dev/null +++ b/lib/features/quick_pay/screens/quick_pay_screen.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/features/quick_pay/screens/quick_pay_outside_bank_screen.dart'; +import 'package:kmobile/features/quick_pay/screens/quick_pay_within_bank_screen.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +class QuickPayScreen extends StatefulWidget { + const QuickPayScreen({super.key}); + + @override + State createState() => _QuickPayScreen(); +} + +class _QuickPayScreen extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Symbols.arrow_back_ios_new), + onPressed: () { + Navigator.pop(context); + }, + ), + title: const Text( + 'Quick Pay', + style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500), + ), + centerTitle: false, + actions: const [ + Padding( + padding: EdgeInsets.only(right: 10.0), + child: CircleAvatar( + backgroundImage: AssetImage('assets/images/avatar.jpg'), + // Replace with your image + radius: 20, + ), + ), + ], + ), + body: ListView( + children: [ + QuickPayManagementTile( + icon: Symbols.input_circle, + label: 'Own Bank', + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const QuickPayWithinBankScreen())); + }, + ), + const Divider( + height: 1, + ), + QuickPayManagementTile( + icon: Symbols.output_circle, + label: 'Outside Bank', + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const QuickPayOutsideBankScreen())); + }, + ), + const Divider( + height: 1, + ), + ], + ), + ); + } +} + +class QuickPayManagementTile extends StatelessWidget { + final IconData icon; + final String label; + final VoidCallback onTap; + + const QuickPayManagementTile({ + super.key, + required this.icon, + required this.label, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: Icon(icon), + title: Text(label), + trailing: const Icon(Symbols.arrow_right, size: 20), + onTap: onTap, + ); + } +} diff --git a/lib/features/quick_pay/screens/quick_pay_within_bank_screen.dart b/lib/features/quick_pay/screens/quick_pay_within_bank_screen.dart new file mode 100644 index 0000000..f5d267e --- /dev/null +++ b/lib/features/quick_pay/screens/quick_pay_within_bank_screen.dart @@ -0,0 +1,207 @@ +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +class QuickPayWithinBankScreen extends StatefulWidget { + const QuickPayWithinBankScreen({super.key}); + + @override + State createState() => _QuickPayWithinBankScreen(); +} + +class _QuickPayWithinBankScreen extends State { + final _formKey = GlobalKey(); + + final TextEditingController accountNumberController = TextEditingController(); + final TextEditingController nameController = TextEditingController(); + final TextEditingController amountController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Symbols.arrow_back_ios_new), + onPressed: () { + Navigator.pop(context); + }, + ), + title: const Text( + 'Quick Pay - Own Bank', + style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500), + ), + centerTitle: false, + actions: const [ + Padding( + padding: EdgeInsets.only(right: 10.0), + child: CircleAvatar( + backgroundImage: AssetImage('assets/images/avatar.jpg'), + // Replace with your image + radius: 20, + ), + ), + ], + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + children: [ + const SizedBox(height: 25), + TextFormField( + decoration: const InputDecoration( + labelText: 'Account Number', + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + controller: accountNumberController, + keyboardType: TextInputType.number, + textInputAction: TextInputAction.next, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Account number is required'; + } else if (value.length != 16) { + return 'Enter a valid account number'; + } + return null; + }, + ), + const Align( + alignment: Alignment.topLeft, + child: Padding( + padding: EdgeInsets.only(left: 15.0, top: 5), + child: Text( + 'Beneficiary Account Number', + style: TextStyle(color: Colors.black54), + ), + )), + const SizedBox(height: 25), + TextFormField( + decoration: const InputDecoration( + labelText: 'Name', + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + controller: nameController, + keyboardType: TextInputType.name, + textInputAction: TextInputAction.next, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Name is required'; + } + return null; + }, + ), + const Align( + alignment: Alignment.topLeft, + child: Padding( + padding: EdgeInsets.only(left: 15.0, top: 5), + child: Text( + 'Beneficiary Name', + style: TextStyle(color: Colors.black54), + ), + )), + const SizedBox(height: 25), + TextFormField( + decoration: const InputDecoration( + labelText: 'Amount', + border: OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + controller: amountController, + keyboardType: TextInputType.number, + textInputAction: TextInputAction.next, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Amount is required'; + } + final amount = double.tryParse(value); + if (amount == null || amount <= 0) { + return 'Enter a valid amount'; + } + return null; + }, + ), + const SizedBox(height: 45), + Align( + alignment: Alignment.center, + child: SizedBox( + width: 250, + height: 50, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: const StadiumBorder(), + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: Colors.blue[900], + foregroundColor: Colors.white, + ), + onPressed: () { + if (_formKey.currentState!.validate()) { + // Perform payment logic + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Processing Payment...')), + ); + } + }, + child: const Text('Pay'), + ), + ), + ), + ], + ), + ), + ), + ); + } + + Widget buildTextFormField({ + required String label, + required TextEditingController controller, + TextInputType keyboardType = TextInputType.text, + String? Function(String?)? validator, + }) { + return TextFormField( + controller: controller, + keyboardType: keyboardType, + validator: validator, + decoration: InputDecoration( + labelText: label, + border: const OutlineInputBorder(), + isDense: true, + filled: true, + fillColor: Colors.white, + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Colors.black, width: 2), + ), + ), + ); + } +}