Manage Beneficiary UI
This commit is contained in:
@@ -17,13 +17,13 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// Automatically go to login after 6 seconds
|
||||
// Timer(const Duration(seconds: 6), () {
|
||||
// Automatically go to logizn after 4 seconds
|
||||
Timer(const Duration(seconds: 4), () {
|
||||
|
||||
|
||||
// }
|
||||
// );
|
||||
widget.onContinue();
|
||||
widget.onContinue();
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
|
@@ -27,7 +27,10 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
|
||||
final TextEditingController ifscController = TextEditingController();
|
||||
final TextEditingController phoneController = TextEditingController();
|
||||
|
||||
|
||||
String? _beneficiaryName;
|
||||
bool _isValidating = false;
|
||||
bool _isBeneficiaryValidated = false;
|
||||
String? _validationError;
|
||||
|
||||
late String accountType;
|
||||
|
||||
@@ -75,6 +78,30 @@ bool _isLoading = false; //for validateIFSC()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> _validateBeneficiary() async {
|
||||
var beneficiaryService = getIt<BeneficiaryService>();
|
||||
setState(() {
|
||||
_isValidating = true;
|
||||
_validationError = null;
|
||||
});
|
||||
try {
|
||||
final name = await beneficiaryService
|
||||
.validateBeneficiaryWithinBank(accountNumberController.text);
|
||||
setState(() {
|
||||
_beneficiaryName = name;
|
||||
_isBeneficiaryValidated = true;
|
||||
_isValidating = false;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_validationError = "Account Number not from KCCB";
|
||||
_isValidating = false;
|
||||
_isBeneficiaryValidated = false;
|
||||
_beneficiaryName = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
String _selectedAccountType = 'Savings'; // default value
|
||||
|
||||
@@ -239,32 +266,6 @@ void validateAndAddBeneficiary() async {
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
TextFormField(
|
||||
controller: nameController,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).name,
|
||||
// prefixIcon: Icon(Icons.person),
|
||||
border: OutlineInputBorder(),
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).dialogBackgroundColor,
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
validator: (value) => value == null || value.isEmpty
|
||||
? AppLocalizations.of(context).nameRequired
|
||||
: null,
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// 🔹 IFSC Code Field
|
||||
TextFormField(
|
||||
@@ -347,10 +348,10 @@ void validateAndAddBeneficiary() async {
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).dialogBackgroundColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2,
|
||||
@@ -359,20 +360,80 @@ void validateAndAddBeneficiary() async {
|
||||
),
|
||||
),
|
||||
|
||||
//validate Beneficiary Button
|
||||
if (!_isBeneficiaryValidated)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: _isValidating
|
||||
? null
|
||||
: () {
|
||||
if (accountNumberController.text.length == 11 &&
|
||||
confirmAccountNumberController.text ==
|
||||
accountNumberController.text) {
|
||||
_validateBeneficiary();
|
||||
} else {
|
||||
setState(() {
|
||||
_validationError =
|
||||
'Please enter a valid and matching account number.';
|
||||
});
|
||||
}
|
||||
},
|
||||
child: _isValidating
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
|
||||
)
|
||||
: const Text('Validate Beneficiary'),
|
||||
),
|
||||
),
|
||||
),
|
||||
//Beneficiary Name (Disabled)
|
||||
const SizedBox(height: 24),
|
||||
TextFormField(
|
||||
controller: nameController,
|
||||
enabled: false,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).beneficiaryName,
|
||||
// prefixIcon: Icon(Icons.person),
|
||||
border: const OutlineInputBorder(),
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).dialogBackgroundColor,
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
validator: (value) => value == null || value.isEmpty
|
||||
? AppLocalizations.of(context).nameRequired
|
||||
: null,
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
// 🔹 Account Type Dropdown
|
||||
DropdownButtonFormField<String>(
|
||||
value: accountType,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).accountType,
|
||||
border: OutlineInputBorder(),
|
||||
border: const OutlineInputBorder(),
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2,
|
||||
@@ -404,15 +465,15 @@ void validateAndAddBeneficiary() async {
|
||||
keyboardType: TextInputType.phone,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).phone,
|
||||
prefixIcon: Icon(Icons.phone),
|
||||
border: OutlineInputBorder(),
|
||||
prefixIcon: const Icon(Icons.phone),
|
||||
border: const OutlineInputBorder(),
|
||||
isDense: true,
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.black),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 2,
|
||||
|
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:confetti/confetti.dart';
|
||||
import 'dart:math';
|
||||
import '../../../app.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
class BeneficiaryResultPage extends StatefulWidget {
|
||||
@@ -65,6 +66,34 @@ class _BeneficiaryResultPageState extends State<BeneficiaryResultPage> {
|
||||
),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
bottom: 20, // keep it slightly above the very bottom
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: SizedBox(
|
||||
height: 56, // larger button height
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pushReplacement( // ensures back goes to ScaffoldScreen
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const NavigationScaffold(),
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const StadiumBorder(),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
backgroundColor: Theme.of(context).primaryColorDark,
|
||||
foregroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: Text(
|
||||
AppLocalizations.of(context).done,
|
||||
style: const TextStyle(fontSize: 18), // slightly bigger text
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.isSuccess)
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
|
@@ -15,89 +15,6 @@ class ManageBeneficiariesScreen extends StatefulWidget {
|
||||
_ManageBeneficiariesScreen();
|
||||
}
|
||||
|
||||
/*class _ManageBeneficiariesScreen extends State<ManageBeneficiariesScreen> {
|
||||
final List<Map<String, String>> beneficiaries = [
|
||||
{'bank': 'State Bank Of India', 'name': 'Trina Bakshi'},
|
||||
{'bank': 'State Bank Of India', 'name': 'Sheetal Rao'},
|
||||
{'bank': 'Punjab National Bank', 'name': 'Manoj Kumar'},
|
||||
{'bank': 'State Bank Of India', 'name': 'Rohit Mehra'},
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: const Icon(Symbols.arrow_back_ios_new),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
AppLocalizations.of(context).beneficiaries,
|
||||
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
|
||||
),
|
||||
centerTitle: false,
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 10.0),
|
||||
child: CircleAvatar(
|
||||
backgroundColor: Colors.grey[200],
|
||||
radius: 20,
|
||||
child: SvgPicture.asset(
|
||||
'assets/images/avatar_male.svg',
|
||||
width: 40,
|
||||
height: 40,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ListView.builder(
|
||||
itemCount: beneficiaries.length,
|
||||
itemBuilder: (context, index) {
|
||||
final beneficiary = beneficiaries[index];
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
child: Text('A'),
|
||||
),
|
||||
title: Text(beneficiary['name']!),
|
||||
subtitle: Text(beneficiary['bank']!),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Symbols.delete_forever, color: Colors.red),
|
||||
onPressed: () {
|
||||
// Delete action
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
floatingActionButton: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: FloatingActionButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const AddBeneficiaryScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: Theme.of(context).primaryColor,
|
||||
elevation: 5,
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
class _ManageBeneficiariesScreen extends State<ManageBeneficiariesScreen> {
|
||||
var service = getIt<BeneficiaryService>();
|
||||
//final BeneficiaryService _service = BeneficiaryService();
|
||||
|
@@ -482,7 +482,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
const FundTransferBeneficiaryScreen()));
|
||||
}, disable: false),
|
||||
}, disable: true),
|
||||
_buildQuickLink(Symbols.server_person,
|
||||
AppLocalizations.of(context).accountInfo, () {
|
||||
Navigator.push(
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
/*import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart';
|
||||
// import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart';
|
||||
import 'package:kmobile/features/fund_transfer/screens/fund_transfer_screen.dart';
|
||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
@@ -84,23 +84,106 @@ class _FundTransferBeneficiaryScreen
|
||||
},
|
||||
),
|
||||
),
|
||||
floatingActionButton: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: FloatingActionButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const AddBeneficiaryScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
backgroundColor: Colors.grey[300],
|
||||
foregroundColor: Theme.of(context).primaryColor,
|
||||
elevation: 5,
|
||||
child: const Icon(Icons.add),
|
||||
);
|
||||
}
|
||||
}*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:kmobile/data/models/beneficiary.dart';
|
||||
//import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
import '../../../di/injection.dart';
|
||||
import 'package:kmobile/api/services/beneficiary_service.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
|
||||
class FundTransferBeneficiaryScreen extends StatefulWidget {
|
||||
const FundTransferBeneficiaryScreen({super.key});
|
||||
|
||||
@override
|
||||
State<FundTransferBeneficiaryScreen> createState() =>
|
||||
_ManageBeneficiariesScreen();
|
||||
}
|
||||
|
||||
class _ManageBeneficiariesScreen extends State<FundTransferBeneficiaryScreen> {
|
||||
var service = getIt<BeneficiaryService>();
|
||||
//final BeneficiaryService _service = BeneficiaryService();
|
||||
bool _isLoading = true;
|
||||
List<Beneficiary> _beneficiaries = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadBeneficiaries();
|
||||
}
|
||||
|
||||
Future<void> _loadBeneficiaries() async {
|
||||
final data = await service.fetchBeneficiaryList();
|
||||
setState(() {
|
||||
_beneficiaries = data ;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildShimmerList() {
|
||||
return ListView.builder(
|
||||
itemCount: 6,
|
||||
itemBuilder: (context, index) => Shimmer.fromColors(
|
||||
baseColor: Colors.grey.shade300,
|
||||
highlightColor: Colors.grey.shade100,
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
title: Container(
|
||||
height: 16,
|
||||
color: Colors.white,
|
||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||
),
|
||||
subtitle: Container(
|
||||
height: 14,
|
||||
color: Colors.white,
|
||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBeneficiaryList() {
|
||||
if (_beneficiaries.isEmpty) {
|
||||
return Center(child: Text(AppLocalizations.of(context).noBeneficiaryFound));
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: _beneficiaries.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _beneficiaries[index];
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.2),
|
||||
child: Text(
|
||||
item.name.isNotEmpty
|
||||
? item.name[0].toUpperCase()
|
||||
: '?',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
title: Text(item.name ?? 'Unknown'),
|
||||
subtitle: Text(item.accountNo ?? 'No account number'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context).beneficiaries),
|
||||
),
|
||||
body: _isLoading ? _buildShimmerList() : _buildBeneficiaryList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -228,6 +228,7 @@
|
||||
"validIfsc": "Valid IFSC",
|
||||
"beneficiaryAddedSuccess": "Beneficiary Added Successfully",
|
||||
"beneficiaryAdditionFailed": "Beneficiary Addition Failed",
|
||||
"noBeneficiaryFound": "No beneficiaries found"
|
||||
"noBeneficiaryFound": "No beneficiaries found",
|
||||
"beneficiaryName": "Beneficiary Name"
|
||||
}
|
||||
|
||||
|
@@ -228,5 +228,6 @@
|
||||
"validIfsc": "मान्य IFSC",
|
||||
"beneficiaryAddedSuccess": "लाभार्थी सफलतापूर्वक जोड़ा गया",
|
||||
"beneficiaryAdditionFailed": "लाभार्थी जोड़ने में विफल",
|
||||
"noBeneficiaryFound": "कोई लाभार्थी नहीं मिला"
|
||||
"noBeneficiaryFound": "कोई लाभार्थी नहीं मिला",
|
||||
"beneficiaryName": "लाभार्थी नाम"
|
||||
}
|
||||
|
@@ -1396,6 +1396,12 @@ abstract class AppLocalizations {
|
||||
/// In en, this message translates to:
|
||||
/// **'No beneficiaries found'**
|
||||
String get noBeneficiaryFound;
|
||||
|
||||
/// No description provided for @beneficiaryName.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Beneficiary Name'**
|
||||
String get beneficiaryName;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
@@ -658,4 +658,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get noBeneficiaryFound => 'No beneficiaries found';
|
||||
|
||||
@override
|
||||
String get beneficiaryName => 'Beneficiary Name';
|
||||
}
|
||||
|
@@ -658,4 +658,7 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get noBeneficiaryFound => 'कोई लाभार्थी नहीं मिला';
|
||||
|
||||
@override
|
||||
String get beneficiaryName => 'लाभार्थी नाम';
|
||||
}
|
||||
|
Reference in New Issue
Block a user