ui improvements
8
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
]
|
||||
}
|
||||
11
assets/.env
@@ -1,6 +1,7 @@
|
||||
# BASE_URL=https://peakapi.qantra.co.zw/api
|
||||
; BASE_URL=https://peakapi.qantra.co.zw/api
|
||||
BASE_URL=http://192.168.100.8:6950/api
|
||||
# BASE_URL=http://192.168.1.164:6950/api
|
||||
# BASE_URL=http://192.168.120.160:6950/api
|
||||
# BASE_URL=http://10.10.2.92:6950/api
|
||||
# BASE_URL=http://172.20.5.105:6950/api
|
||||
; BASE_URL=http://192.168.1.164:6950/api
|
||||
; BASE_URL=http://192.168.120.160:6950/api
|
||||
; BASE_URL=http://10.10.2.92:6950/api
|
||||
; BASE_URL=http://172.20.5.105:6950/api
|
||||
; BASE_URL=http://10.10.3.92:6950/api
|
||||
BIN
assets/apple.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/google.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/landing.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
assets/password.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
assets/united-states.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/verify.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
assets/zimbabwe.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
@@ -3,6 +3,11 @@ import 'package:provider/provider.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:qpay/screens/confirm/confirm_screen.dart';
|
||||
import 'package:qpay/screens/gateway/gateway_screen.dart';
|
||||
import 'package:qpay/screens/onboarding/landing/landing_screen.dart';
|
||||
import 'package:qpay/screens/onboarding/login/login_screen.dart';
|
||||
import 'package:qpay/screens/onboarding/password/password_screen.dart';
|
||||
import 'package:qpay/screens/onboarding/register/register_screen.dart';
|
||||
import 'package:qpay/screens/onboarding/verify/verify_screen.dart';
|
||||
import 'package:qpay/screens/pay/pay_screen.dart';
|
||||
import 'package:qpay/screens/receipt/receipt_screen.dart';
|
||||
import 'package:qpay/screens/recipient/recipients_screen.dart';
|
||||
@@ -65,6 +70,11 @@ class _MyAppState extends State<MyApp> {
|
||||
routes: [
|
||||
ShellRoute(
|
||||
builder: (context, state, child) {
|
||||
// If the current location is an onboarding route, don't show the navbar
|
||||
final location = GoRouterState.of(context).uri.toString();
|
||||
if (location.startsWith('/onboarding/')) {
|
||||
return child;
|
||||
}
|
||||
return ScaffoldWithNavBar(child: child);
|
||||
},
|
||||
routes: [
|
||||
@@ -102,6 +112,26 @@ class _MyAppState extends State<MyApp> {
|
||||
path: '/profile',
|
||||
builder: (context, state) => const ProfileScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/onboarding/landing',
|
||||
builder: (context, state) => const LandingScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/onboarding/register',
|
||||
builder: (context, state) => const RegisterScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/onboarding/verify',
|
||||
builder: (context, state) => const VerifyScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/onboarding/password',
|
||||
builder: (context, state) => const PasswordScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/onboarding/login',
|
||||
builder: (context, state) => const LoginScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -62,11 +62,12 @@ class ConfirmController extends ChangeNotifier {
|
||||
.formData = transactionController.model.formData.copyWith(
|
||||
type: "REQUEST",
|
||||
trace: transactionController.model.confirmationData['trace'],
|
||||
id: transactionController.model.confirmationData['id'],
|
||||
authType: transactionController.model.selectedPaymentProcessor.authType,
|
||||
);
|
||||
|
||||
dynamic response = await http.post(
|
||||
'/transaction',
|
||||
'/public/transaction',
|
||||
transactionController.model.formData,
|
||||
);
|
||||
logger.i(response.toString());
|
||||
@@ -107,12 +108,11 @@ class ConfirmController extends ChangeNotifier {
|
||||
|
||||
Future<void> pollTransaction(String uid) async {
|
||||
while (!model.isCancelled) {
|
||||
|
||||
// Check again after delay in case it was cancelled during the delay
|
||||
if (model.isCancelled) break;
|
||||
|
||||
try {
|
||||
dynamic response = await http.get('/transaction/poll/$uid');
|
||||
dynamic response = await http.get('/public/transaction/poll/$uid');
|
||||
logger.i(response.toString());
|
||||
|
||||
if (response['status'] == 'SUCCESS') {
|
||||
|
||||
@@ -200,6 +200,13 @@ class _ConfirmScreenState extends State<ConfirmScreen>
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if (controller
|
||||
.transactionController
|
||||
.model
|
||||
.confirmationData["charge"] !=
|
||||
0)
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
@@ -222,6 +229,15 @@ class _ConfirmScreenState extends State<ConfirmScreen>
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
if (controller
|
||||
.transactionController
|
||||
.model
|
||||
.confirmationData["gatewayCharge"] !=
|
||||
0)
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
@@ -244,6 +260,15 @@ class _ConfirmScreenState extends State<ConfirmScreen>
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
if (controller
|
||||
.transactionController
|
||||
.model
|
||||
.confirmationData["tax"] !=
|
||||
0)
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
@@ -266,6 +291,8 @@ class _ConfirmScreenState extends State<ConfirmScreen>
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
@@ -287,11 +314,28 @@ class _ConfirmScreenState extends State<ConfirmScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
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('/');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -58,7 +58,7 @@ class GatewayController extends ChangeNotifier {
|
||||
if (model.isCancelled) break;
|
||||
|
||||
try {
|
||||
dynamic response = await http.get('/transaction/poll/$uid');
|
||||
dynamic response = await http.get('/public/transaction/poll/$uid');
|
||||
logger.i(response.toString());
|
||||
|
||||
if (response['status'] == 'SUCCESS') {
|
||||
@@ -85,7 +85,7 @@ class GatewayController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
try {
|
||||
dynamic response = await http.get('/transaction/poll/$uid');
|
||||
dynamic response = await http.get('/public/transaction/poll/$uid');
|
||||
logger.i(response.toString());
|
||||
|
||||
if (response['status'] == 'SUCCESS') {
|
||||
|
||||
@@ -37,7 +37,7 @@ class HistoryController extends ChangeNotifier {
|
||||
try {
|
||||
params['type'] = 'REQUEST';
|
||||
Map<String, dynamic> response = await http.get(
|
||||
'/transaction?${buildQueryParameters(params)}',
|
||||
'/public/transaction?${buildQueryParameters(params)}',
|
||||
);
|
||||
|
||||
if (params['page'] == '0') {
|
||||
|
||||
@@ -11,6 +11,7 @@ class HomeScreenModel {
|
||||
List<Category> categories = [];
|
||||
List<BillProvider> providers = [];
|
||||
List<BillProvider> filterableProviders = [];
|
||||
List<Account> accounts = [];
|
||||
BillProvider? selectedProvider;
|
||||
dynamic selectedCategory;
|
||||
dynamic account = {};
|
||||
@@ -20,6 +21,26 @@ class HomeScreenModel {
|
||||
String? errorMessage;
|
||||
}
|
||||
|
||||
class Account {
|
||||
late String name;
|
||||
late String description;
|
||||
late String image;
|
||||
late String currency;
|
||||
late String balance;
|
||||
late String availableBalance;
|
||||
late String accountNumber;
|
||||
late String accountType;
|
||||
late String accountHolderName;
|
||||
|
||||
Account({
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.image,
|
||||
required this.currency,
|
||||
required this.balance,
|
||||
});
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class Category with _$Category {
|
||||
const factory Category({
|
||||
@@ -76,12 +97,17 @@ class HomeController extends ChangeNotifier {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> getAccounts() async {
|
||||
model.accounts = getFakeAccounts();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getProviders() async {
|
||||
try {
|
||||
model.isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
List<dynamic> response = await http.get('/providers');
|
||||
List<dynamic> response = await http.get('/public/providers');
|
||||
model.providers = response.map((e) => BillProvider.fromJson(e)).toList();
|
||||
model.filterableProviders = model.providers;
|
||||
|
||||
@@ -116,7 +142,7 @@ class HomeController extends ChangeNotifier {
|
||||
|
||||
try {
|
||||
Map<String, dynamic> response = await http.get(
|
||||
'/transaction?sort=createdAt,desc&size=3&page=0&userId=$userId&type=REQUEST',
|
||||
'/public/transaction?sort=createdAt,desc&size=3&page=0&userId=$userId&type=REQUEST',
|
||||
);
|
||||
|
||||
PageableModel pageableModel = PageableModel.fromJson(response);
|
||||
@@ -146,7 +172,7 @@ class HomeController extends ChangeNotifier {
|
||||
|
||||
// Simulate fetching categories
|
||||
try {
|
||||
List<dynamic> response = await http.get('/categories');
|
||||
List<dynamic> response = await http.get('/public/categories');
|
||||
model.categories = response.map((e) => Category.fromJson(e)).toList();
|
||||
|
||||
if (model.categories.isNotEmpty) {
|
||||
@@ -246,4 +272,23 @@ class HomeController extends ChangeNotifier {
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<Account> getFakeAccounts() {
|
||||
return [
|
||||
Account(
|
||||
name: 'Vusumuzi Khoza',
|
||||
description: 'USD Wallet',
|
||||
image: 'united-states.png',
|
||||
currency: 'USD',
|
||||
balance: '4.02',
|
||||
),
|
||||
Account(
|
||||
name: 'Vusumuzi Khoza',
|
||||
description: 'ZWG Wallet',
|
||||
image: 'zimbabwe.png',
|
||||
currency: 'ZWL',
|
||||
balance: '36.00',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
|
||||
Future<void> setupDataAndTransitions() async {
|
||||
homeController = HomeController(context);
|
||||
await homeController.getAccounts();
|
||||
await homeController.getCategories();
|
||||
await homeController.getProviders();
|
||||
|
||||
@@ -108,20 +109,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
}
|
||||
|
||||
getBoxDecoration(String? clientId) {
|
||||
// BillProvider? provider = homeController.model.selectedProvider;
|
||||
//
|
||||
// if (provider != null && clientId != provider.clientId) {
|
||||
// return BoxDecoration(
|
||||
// border: Border.all(color: Colors.black87.withAlpha(20)),
|
||||
// borderRadius: BorderRadius.circular(12),
|
||||
// );
|
||||
// }
|
||||
|
||||
return BoxDecoration(
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.7),
|
||||
// width: 3,
|
||||
// ),
|
||||
border: Border.all(color: Colors.black87.withAlpha(20)),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
gradient: LinearGradient(
|
||||
@@ -161,12 +149,45 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
surfaceTintColor: Colors.transparent,
|
||||
floating: true,
|
||||
title: Image(image: AssetImage('assets/peak.png'), width: 85),
|
||||
actions: [
|
||||
leading: IconButton(
|
||||
icon: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.black87.withAlpha(20)),
|
||||
shape: BoxShape.circle,
|
||||
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: Center(
|
||||
child: Text(
|
||||
'VK',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () {},
|
||||
),
|
||||
// actions: [
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.notifications_outlined),
|
||||
// onPressed: () {},
|
||||
// ),
|
||||
],
|
||||
// ],
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
@@ -177,29 +198,8 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (_isLoggedIn)
|
||||
AlignTransition(
|
||||
alignment: _alignAnimation,
|
||||
child: Skeletonizer(
|
||||
enabled: _loading,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Vusumuzi Khoza",
|
||||
style: TextStyle(fontSize: 15),
|
||||
),
|
||||
Text(
|
||||
"USD 4.02",
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!_isLoggedIn) _buildLogin(homeController),
|
||||
if (_isLoggedIn) _buildAccounts(homeController),
|
||||
_buildFilterChips(homeController),
|
||||
SizedBox(height: 10),
|
||||
Column(
|
||||
@@ -209,7 +209,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
@@ -273,6 +273,174 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLogin(HomeController controller) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Register or Login for more',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_showFeaturesPopup(context);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2),
|
||||
child: Text(
|
||||
'features',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: Theme.of(context).colorScheme.primary,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
context.push('/onboarding/landing');
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text('Get Started', style: TextStyle(fontSize: 12)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Divider(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.2),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAccounts(HomeController controller) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
...controller.model.accounts.map(
|
||||
(e) => Row(
|
||||
children: [
|
||||
Skeletonizer(
|
||||
enabled: controller.model.isLoading,
|
||||
child: SlideTransition(
|
||||
position: _alignListAnimations[0],
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: () {
|
||||
// context.push("/wallet");
|
||||
},
|
||||
child: Container(
|
||||
width: 150,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 12,
|
||||
),
|
||||
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: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Image.asset('assets/${e.image}', width: 20),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
e.currency,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w100,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Text(
|
||||
e.balance,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
// context.push("/withdraw");
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary
|
||||
.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
'Add Money',
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTransactionItem(Map<String, dynamic> e, BuildContext context) {
|
||||
return SlideTransition(
|
||||
position: _alignListAnimations[0],
|
||||
@@ -463,4 +631,197 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showFeaturesPopup(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
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.withOpacity(0.1),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header with icon and title
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withValues(alpha: 0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Image(image: AssetImage("assets/peak.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.withOpacity(0.7),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Features list
|
||||
_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),
|
||||
|
||||
// Action buttons
|
||||
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.withOpacity(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: 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.withOpacity(0.7),
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
90
lib/screens/onboarding/landing/landing_screen.dart
Normal file
@@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:qpay/screens/onboarding/login/login_controller.dart';
|
||||
|
||||
class LandingScreen extends StatefulWidget {
|
||||
const LandingScreen({super.key});
|
||||
|
||||
@override
|
||||
State<LandingScreen> createState() => _LandingScreenState();
|
||||
}
|
||||
|
||||
class _LandingScreenState extends State<LandingScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(25),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 75),
|
||||
Text(
|
||||
'Welcome to Peak',
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
'The fastest way to pay for local goods and services',
|
||||
style: TextStyle(fontSize: 18),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Image.asset('assets/landing.png', width: 300),
|
||||
Image.asset('assets/peak.png', width: 100),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
'Register or login to get started',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: SizedBox(
|
||||
height: 130,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
context.go('/onboarding/register');
|
||||
},
|
||||
child: Text('Register', style: TextStyle(fontSize: 16)),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
context.go('/onboarding/login');
|
||||
},
|
||||
child: Text('Login', style: TextStyle(fontSize: 16)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.go('/');
|
||||
},
|
||||
child: Text('Continue as guest?', style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
80
lib/screens/onboarding/login/login_controller.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qpay/http/http.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class LoginScreenModel {
|
||||
String username = "";
|
||||
String password = "";
|
||||
String status = "PENDING";
|
||||
|
||||
bool isLoading = false;
|
||||
String? errorMessage;
|
||||
}
|
||||
|
||||
class LoginController extends ChangeNotifier {
|
||||
final Http http = Http();
|
||||
final model = LoginScreenModel();
|
||||
late SharedPreferences prefs;
|
||||
BuildContext context;
|
||||
|
||||
LoginController(this.context);
|
||||
|
||||
void _showErrorSnackBar(String? message) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message.toString()),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
backgroundColor: Colors.deepOrange,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<LoginScreenModel> login(String username, String password) async {
|
||||
try {
|
||||
model.isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
prefs = await SharedPreferences.getInstance();
|
||||
|
||||
dynamic response = await http.post('/auth/login', {
|
||||
'username': username,
|
||||
'password': password,
|
||||
});
|
||||
logger.i(response.toString());
|
||||
|
||||
if (response.containsKey('token')) {
|
||||
model.status = 'SUCCESS';
|
||||
|
||||
prefs.setString('token', response['token']);
|
||||
prefs.setString('username', username);
|
||||
prefs.setString('email', response['email']);
|
||||
prefs.setString('firstName', response['firstName']);
|
||||
prefs.setString('lastName', response['lastName']);
|
||||
prefs.setString('phone', response['phone']);
|
||||
|
||||
return model;
|
||||
} else {
|
||||
model.errorMessage =
|
||||
response['errorMessage'] ??
|
||||
"Problem with the transaction. Please try again or contact support";
|
||||
_showErrorSnackBar(model.errorMessage);
|
||||
}
|
||||
} on DioException catch (e, s) {
|
||||
logger.e(s);
|
||||
_showErrorSnackBar("Network error. Please try again or contact support");
|
||||
} catch (e, s) {
|
||||
logger.e(s);
|
||||
_showErrorSnackBar(
|
||||
"Problem processing that transaction. Please try again or contact support",
|
||||
);
|
||||
}
|
||||
|
||||
model.isLoading = false;
|
||||
notifyListeners();
|
||||
|
||||
return model;
|
||||
}
|
||||
}
|
||||
231
lib/screens/onboarding/login/login_screen.dart
Normal file
@@ -0,0 +1,231 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:qpay/screens/onboarding/login/login_controller.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
|
||||
@override
|
||||
State<LoginScreen> createState() => _LoginScreenState();
|
||||
}
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _usernameController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
|
||||
late LoginController _loginController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loginController = LoginController(context);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_loginController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(25),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 75),
|
||||
Text(
|
||||
'Login',
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
'Welcome back to Peak',
|
||||
style: TextStyle(fontSize: 18),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
// Username Field
|
||||
TextFormField(
|
||||
controller: _usernameController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Phone Number',
|
||||
prefixIcon: Icon(Icons.person_outline),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please enter a phone number';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// Password Field
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Password',
|
||||
prefixIcon: Icon(Icons.lock_outline),
|
||||
suffixIcon: Icon(Icons.visibility_outlined),
|
||||
),
|
||||
obscureText: true,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please enter a password';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// Forgot Password Link
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
// TODO: Add forgot password logic
|
||||
},
|
||||
child: Text(
|
||||
'Forgot Password?',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
// Divider with "or" text
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Divider(color: Colors.grey.shade300)),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
'or continue with',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade600,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(child: Divider(color: Colors.grey.shade300)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
// Social Login Buttons
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
// TODO: Add Google login logic
|
||||
},
|
||||
icon: Image.asset(
|
||||
'assets/google.png',
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
label: Text('Google', style: TextStyle(fontSize: 16)),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
side: BorderSide(color: Colors.grey.shade300),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
// TODO: Add Apple login logic
|
||||
},
|
||||
icon: Image.asset(
|
||||
'assets/apple.png',
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
label: Text('Apple', style: TextStyle(fontSize: 16)),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
side: BorderSide(color: Colors.grey.shade300),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: SizedBox(
|
||||
height: 130,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
// TODO: Add login logic
|
||||
if (_formKey.currentState!.validate()) {
|
||||
LoginScreenModel model = await _loginController.login(
|
||||
_usernameController.text,
|
||||
_passwordController.text,
|
||||
);
|
||||
|
||||
if (model.status == 'SUCCESS') {
|
||||
context.go('/');
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text('Login', style: TextStyle(fontSize: 16)),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
context.go('/onboarding/register');
|
||||
},
|
||||
child: Text(
|
||||
'Create Account',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.go('/');
|
||||
},
|
||||
child: Text('Continue as guest?', style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
255
lib/screens/onboarding/password/password_screen.dart
Normal file
@@ -0,0 +1,255 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class PasswordScreen extends StatefulWidget {
|
||||
const PasswordScreen({super.key});
|
||||
|
||||
@override
|
||||
State<PasswordScreen> createState() => _PasswordScreenState();
|
||||
}
|
||||
|
||||
class _PasswordScreenState extends State<PasswordScreen> {
|
||||
final TextEditingController _passwordController = TextEditingController();
|
||||
final TextEditingController _confirmPasswordController =
|
||||
TextEditingController();
|
||||
final FocusNode _passwordFocusNode = FocusNode();
|
||||
final FocusNode _confirmPasswordFocusNode = FocusNode();
|
||||
bool _obscurePassword = true;
|
||||
bool _obscureConfirmPassword = true;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_passwordController.dispose();
|
||||
_confirmPasswordController.dispose();
|
||||
_passwordFocusNode.dispose();
|
||||
_confirmPasswordFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(25),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 75),
|
||||
Text(
|
||||
'Set New Password',
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
'Create a strong password to secure your account',
|
||||
style: TextStyle(fontSize: 18),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Image.asset('assets/password.png', width: 300),
|
||||
// Password Input
|
||||
TextField(
|
||||
controller: _passwordController,
|
||||
focusNode: _passwordFocusNode,
|
||||
obscureText: _obscurePassword,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'New Password',
|
||||
hintText: 'Enter your new password',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscurePassword
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscurePassword = !_obscurePassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// Confirm Password Input
|
||||
TextField(
|
||||
controller: _confirmPasswordController,
|
||||
focusNode: _confirmPasswordFocusNode,
|
||||
obscureText: _obscureConfirmPassword,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Confirm Password',
|
||||
hintText: 'Confirm your new password',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscureConfirmPassword
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscureConfirmPassword = !_obscureConfirmPassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// Password Requirements
|
||||
Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade50,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.grey.shade200),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Password Requirements:',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
_buildRequirement('At least 8 characters', true),
|
||||
_buildRequirement('Contains uppercase letter', true),
|
||||
_buildRequirement('Contains lowercase letter', true),
|
||||
_buildRequirement('Contains number', true),
|
||||
_buildRequirement('Contains special character', false),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: SizedBox(
|
||||
height: 130,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
// TODO: Add password update logic
|
||||
String password = _passwordController.text;
|
||||
String confirmPassword =
|
||||
_confirmPasswordController.text;
|
||||
print('Password: $password');
|
||||
print('Confirm Password: $confirmPassword');
|
||||
|
||||
context.go('/onboarding/login');
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Update Password',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
context.go('/onboarding/login');
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
side: BorderSide(color: Colors.grey.shade300),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Back to Login',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.go('/');
|
||||
},
|
||||
child: Text('Continue as guest?', style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRequirement(String text, bool isMet) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
isMet ? Icons.check_circle : Icons.circle_outlined,
|
||||
size: 16,
|
||||
color: isMet ? Colors.green : Colors.grey.shade600,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isMet ? Colors.grey.shade600 : Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
194
lib/screens/onboarding/register/register_screen.dart
Normal file
@@ -0,0 +1,194 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class RegisterScreen extends StatefulWidget {
|
||||
const RegisterScreen({super.key});
|
||||
|
||||
@override
|
||||
State<RegisterScreen> createState() => _RegisterScreenState();
|
||||
}
|
||||
|
||||
class _RegisterScreenState extends State<RegisterScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(25),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 75),
|
||||
Text(
|
||||
'Registration',
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
'Join Peak and start making fast payments',
|
||||
style: TextStyle(fontSize: 18),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
// First Name Field
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: 'First Name',
|
||||
prefixIcon: Icon(Icons.person_outline),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// Last Name Field
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Last Name',
|
||||
prefixIcon: Icon(Icons.person_outline),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// Phone Field
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Phone Number',
|
||||
prefixIcon: Icon(Icons.phone_outlined),
|
||||
),
|
||||
keyboardType: TextInputType.phone,
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// Email Field
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Email Address',
|
||||
prefixIcon: Icon(Icons.email_outlined),
|
||||
),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// Password Field
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Password',
|
||||
prefixIcon: Icon(Icons.lock_outline),
|
||||
suffixIcon: Icon(Icons.visibility_outlined),
|
||||
),
|
||||
obscureText: true,
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
// Divider with "or" text
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Divider(color: Colors.grey.shade300)),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
'or continue with',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade600,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(child: Divider(color: Colors.grey.shade300)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
// Social Login Buttons
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
// TODO: Add Google login logic
|
||||
},
|
||||
icon: Image.asset(
|
||||
'assets/google.png',
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
label: Text('Google', style: TextStyle(fontSize: 16)),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
side: BorderSide(color: Colors.grey.shade300),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
// TODO: Add Apple login logic
|
||||
},
|
||||
icon: Image.asset(
|
||||
'assets/apple.png',
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
label: Text('Apple', style: TextStyle(fontSize: 16)),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
side: BorderSide(color: Colors.grey.shade300),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: SizedBox(
|
||||
height: 130,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
context.go('/onboarding/verify');
|
||||
},
|
||||
child: Text('Submit', style: TextStyle(fontSize: 16)),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
context.go('/onboarding/login');
|
||||
},
|
||||
child: Text(
|
||||
'Go to Login',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.go('/');
|
||||
},
|
||||
child: Text('Continue as guest?', style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
188
lib/screens/onboarding/verify/verify_screen.dart
Normal file
@@ -0,0 +1,188 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class VerifyScreen extends StatefulWidget {
|
||||
const VerifyScreen({super.key});
|
||||
|
||||
@override
|
||||
State<VerifyScreen> createState() => _VerifyScreenState();
|
||||
}
|
||||
|
||||
class _VerifyScreenState extends State<VerifyScreen> {
|
||||
final List<TextEditingController> _codeControllers = List.generate(
|
||||
6,
|
||||
(index) => TextEditingController(),
|
||||
);
|
||||
final List<FocusNode> _focusNodes = List.generate(6, (index) => FocusNode());
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (var controller in _codeControllers) {
|
||||
controller.dispose();
|
||||
}
|
||||
for (var node in _focusNodes) {
|
||||
node.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onCodeChanged(String value, int index) {
|
||||
if (value.length == 1 && index < 5) {
|
||||
_focusNodes[index + 1].requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(25),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 75),
|
||||
Text(
|
||||
'Verify Your Account',
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
'Enter the 6-digit code sent to your phone/email',
|
||||
style: TextStyle(fontSize: 18),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Image.asset('assets/verify.png', width: 300),
|
||||
// Verification Code Input
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: List.generate(
|
||||
6,
|
||||
(index) => SizedBox(
|
||||
width: 50,
|
||||
child: TextField(
|
||||
controller: _codeControllers[index],
|
||||
focusNode: _focusNodes[index],
|
||||
textAlign: TextAlign.center,
|
||||
keyboardType: TextInputType.number,
|
||||
maxLength: 1,
|
||||
decoration: InputDecoration(
|
||||
counterText: '',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
onChanged: (value) => _onCodeChanged(value, index),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
// Resend Code Section
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"Didn't receive the code? ",
|
||||
style: TextStyle(color: Colors.grey.shade600, fontSize: 16),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// TODO: Add resend code logic
|
||||
},
|
||||
child: Text(
|
||||
'Resend Code',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// Timer for resend (optional)
|
||||
Text(
|
||||
'Resend available in 2:30',
|
||||
style: TextStyle(color: Colors.grey.shade500, fontSize: 14),
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: SizedBox(
|
||||
height: 130,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
context.go('/onboarding/password');
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Verify Code',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
context.go('/onboarding/login');
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
side: BorderSide(color: Colors.grey.shade300),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Back to Login',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.go('/');
|
||||
},
|
||||
child: Text('Continue as guest?', style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ class PayController extends ChangeNotifier {
|
||||
);
|
||||
|
||||
dynamic response = await http.post(
|
||||
'/transaction',
|
||||
'/public/transaction',
|
||||
transactionController.model.formData,
|
||||
);
|
||||
logger.i(response.toString());
|
||||
@@ -148,7 +148,7 @@ class PayController extends ChangeNotifier {
|
||||
model.isLoading = true;
|
||||
|
||||
List<dynamic> response = await http.get(
|
||||
'/providers/$providerId/products',
|
||||
'/public/providers/$providerId/products',
|
||||
);
|
||||
model.products = response.map((e) => BillProduct.fromJson(e)).toList();
|
||||
} catch (e) {
|
||||
@@ -168,7 +168,7 @@ class PayController extends ChangeNotifier {
|
||||
model.paymentProcessors = getFakePaymentProcessors();
|
||||
notifyListeners();
|
||||
|
||||
List<dynamic> response = await http.get('/payment-processors');
|
||||
List<dynamic> response = await http.get('/public/payment-processors');
|
||||
model.paymentProcessors =
|
||||
response.map((e) => PaymentProcessor.fromJson(e)).toList();
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class ReceiptController extends ChangeNotifier {
|
||||
// fetch provider & update tran controller
|
||||
String providerId = model.receiptData?['billClientId'] ?? '';
|
||||
Map<String, dynamic> provider = await http.get(
|
||||
'/providers/client/$providerId',
|
||||
'/public/providers/client/$providerId',
|
||||
);
|
||||
transactionController.model.selectedProvider = hc.BillProvider.fromJson(
|
||||
provider,
|
||||
@@ -72,7 +72,7 @@ class ReceiptController extends ChangeNotifier {
|
||||
|
||||
if (productUid.isNotEmpty) {
|
||||
Map<String, dynamic> product = await http.get(
|
||||
'/providers/$providerUid/products/$productUid',
|
||||
'/public/providers/$providerUid/products/$productUid',
|
||||
);
|
||||
transactionController.model.selectedProduct = pc.BillProduct.fromJson(
|
||||
product,
|
||||
@@ -96,7 +96,7 @@ class ReceiptController extends ChangeNotifier {
|
||||
Future<void> getReceiptData(String id) async {
|
||||
setLoading(true);
|
||||
try {
|
||||
Map<String, dynamic> response = await http.get('/transaction/$id');
|
||||
Map<String, dynamic> response = await http.get('/public/transaction/$id');
|
||||
model.receiptData = response;
|
||||
} catch (e) {
|
||||
_showErrorSnackBar(
|
||||
|
||||
@@ -212,7 +212,7 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
Text(
|
||||
receiptController
|
||||
.model
|
||||
.receiptData?["totalAmount"]
|
||||
.receiptData?["amount"]
|
||||
.toStringAsFixed(2) ??
|
||||
"",
|
||||
style: TextStyle(fontSize: 26),
|
||||
@@ -447,6 +447,37 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Skeletonizer(
|
||||
enabled:
|
||||
receiptController
|
||||
.model
|
||||
.isLoading,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Debit Reference",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
transactionController
|
||||
.model
|
||||
.receiptData?["debitRef"] ??
|
||||
"",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Skeletonizer(
|
||||
enabled:
|
||||
receiptController
|
||||
@@ -613,6 +644,12 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if (transactionController
|
||||
.model
|
||||
.receiptData?["charge"] !=
|
||||
0)
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
@@ -623,7 +660,8 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
FontWeight
|
||||
.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
@@ -640,7 +678,17 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (transactionController
|
||||
.model
|
||||
.receiptData?["gatewayCharge"] !=
|
||||
0)
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
@@ -651,7 +699,8 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
FontWeight
|
||||
.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
@@ -668,7 +717,17 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (transactionController
|
||||
.model
|
||||
.receiptData?["tax"] !=
|
||||
0)
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
@@ -679,7 +738,8 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
FontWeight
|
||||
.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
@@ -696,7 +756,11 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
|
||||
@@ -67,7 +67,7 @@ class RecipientsController extends ChangeNotifier {
|
||||
|
||||
try {
|
||||
List<dynamic> response = await http.get(
|
||||
'/recipients/search?${buildQueryParameters(params ?? {})}',
|
||||
'/public/recipients/search?${buildQueryParameters(params ?? {})}',
|
||||
);
|
||||
model.recipients.clear();
|
||||
model.recipients.addAll(response.map((e) => Recipient.fromJson(e)));
|
||||
|
||||
@@ -15,6 +15,7 @@ class TransactionModel {
|
||||
BillProvider? selectedProvider;
|
||||
Map<String, dynamic>? receiptData;
|
||||
FormData formData = FormData(
|
||||
id: '',
|
||||
type: '',
|
||||
billClientId: '',
|
||||
debitRef: '',
|
||||
@@ -49,6 +50,7 @@ abstract class FormData with _$FormData {
|
||||
const factory FormData({
|
||||
required String type, // CONFIRM, REQUEST, REVERSE
|
||||
required String billClientId,
|
||||
required String id,
|
||||
required String debitRef,
|
||||
required String debitCurrency,
|
||||
required String amount,
|
||||
|
||||
@@ -17,7 +17,7 @@ T _$identity<T>(T value) => value;
|
||||
mixin _$FormData {
|
||||
|
||||
String get type;// CONFIRM, REQUEST, REVERSE
|
||||
String get billClientId; String get debitRef; String get debitCurrency; String get amount; String get creditAccount; String? get creditPhone; String? get creditName; String? get creditEmail; String get billName; String? get errorMessage; String get status; String get paymentProcessorLabel; String get paymentProcessorName; String get paymentProcessorImage; String get providerImage; String get providerLabel; String get userId; String? get debitPhone; String? get debitAccount; String? get productUid; String? get trace; String? get authType; String? get charge; String? get gatewayCharge; String? get tax; String? get totalAmount;
|
||||
String get billClientId; String get id; String get debitRef; String get debitCurrency; String get amount; String get creditAccount; String? get creditPhone; String? get creditName; String? get creditEmail; String get billName; String? get errorMessage; String get status; String get paymentProcessorLabel; String get paymentProcessorName; String get paymentProcessorImage; String get providerImage; String get providerLabel; String get userId; String? get debitPhone; String? get debitAccount; String? get productUid; String? get trace; String? get authType; String? get charge; String? get gatewayCharge; String? get tax; String? get totalAmount;
|
||||
/// Create a copy of FormData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -30,16 +30,16 @@ $FormDataCopyWith<FormData> get copyWith => _$FormDataCopyWithImpl<FormData>(thi
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is FormData&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is FormData&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.id, id) || other.id == id)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hashAll([runtimeType,type,billClientId,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]);
|
||||
int get hashCode => Object.hashAll([runtimeType,type,billClientId,id,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FormData(type: $type, billClientId: $billClientId, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)';
|
||||
return 'FormData(type: $type, billClientId: $billClientId, id: $id, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)';
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ abstract mixin class $FormDataCopyWith<$Res> {
|
||||
factory $FormDataCopyWith(FormData value, $Res Function(FormData) _then) = _$FormDataCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String type, String billClientId, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount
|
||||
String type, String billClientId, String id, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount
|
||||
});
|
||||
|
||||
|
||||
@@ -67,10 +67,11 @@ class _$FormDataCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of FormData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? billClientId = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? billClientId = null,Object? id = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||
as String,billClientId: null == billClientId ? _self.billClientId : billClientId // ignore: cast_nullable_to_non_nullable
|
||||
as String,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,debitRef: null == debitRef ? _self.debitRef : debitRef // ignore: cast_nullable_to_non_nullable
|
||||
as String,debitCurrency: null == debitCurrency ? _self.debitCurrency : debitCurrency // ignore: cast_nullable_to_non_nullable
|
||||
as String,amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable
|
||||
@@ -107,12 +108,13 @@ as String?,
|
||||
@JsonSerializable()
|
||||
|
||||
class _FormData implements FormData {
|
||||
const _FormData({required this.type, required this.billClientId, required this.debitRef, required this.debitCurrency, required this.amount, required this.creditAccount, required this.creditPhone, required this.creditName, required this.creditEmail, required this.billName, required this.errorMessage, required this.status, required this.paymentProcessorLabel, required this.paymentProcessorName, required this.paymentProcessorImage, required this.providerImage, required this.providerLabel, required this.userId, this.debitPhone, this.debitAccount, this.productUid, this.trace, this.authType, this.charge, this.gatewayCharge, this.tax, this.totalAmount});
|
||||
const _FormData({required this.type, required this.billClientId, required this.id, required this.debitRef, required this.debitCurrency, required this.amount, required this.creditAccount, required this.creditPhone, required this.creditName, required this.creditEmail, required this.billName, required this.errorMessage, required this.status, required this.paymentProcessorLabel, required this.paymentProcessorName, required this.paymentProcessorImage, required this.providerImage, required this.providerLabel, required this.userId, this.debitPhone, this.debitAccount, this.productUid, this.trace, this.authType, this.charge, this.gatewayCharge, this.tax, this.totalAmount});
|
||||
factory _FormData.fromJson(Map<String, dynamic> json) => _$FormDataFromJson(json);
|
||||
|
||||
@override final String type;
|
||||
// CONFIRM, REQUEST, REVERSE
|
||||
@override final String billClientId;
|
||||
@override final String id;
|
||||
@override final String debitRef;
|
||||
@override final String debitCurrency;
|
||||
@override final String amount;
|
||||
@@ -152,16 +154,16 @@ Map<String, dynamic> toJson() {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _FormData&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _FormData&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.id, id) || other.id == id)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hashAll([runtimeType,type,billClientId,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]);
|
||||
int get hashCode => Object.hashAll([runtimeType,type,billClientId,id,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FormData(type: $type, billClientId: $billClientId, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)';
|
||||
return 'FormData(type: $type, billClientId: $billClientId, id: $id, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)';
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +174,7 @@ abstract mixin class _$FormDataCopyWith<$Res> implements $FormDataCopyWith<$Res>
|
||||
factory _$FormDataCopyWith(_FormData value, $Res Function(_FormData) _then) = __$FormDataCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String type, String billClientId, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount
|
||||
String type, String billClientId, String id, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount
|
||||
});
|
||||
|
||||
|
||||
@@ -189,10 +191,11 @@ class __$FormDataCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of FormData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? billClientId = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? billClientId = null,Object? id = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) {
|
||||
return _then(_FormData(
|
||||
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||
as String,billClientId: null == billClientId ? _self.billClientId : billClientId // ignore: cast_nullable_to_non_nullable
|
||||
as String,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,debitRef: null == debitRef ? _self.debitRef : debitRef // ignore: cast_nullable_to_non_nullable
|
||||
as String,debitCurrency: null == debitCurrency ? _self.debitCurrency : debitCurrency // ignore: cast_nullable_to_non_nullable
|
||||
as String,amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable
|
||||
|
||||
@@ -9,6 +9,7 @@ part of 'transaction_controller.dart';
|
||||
_FormData _$FormDataFromJson(Map<String, dynamic> json) => _FormData(
|
||||
type: json['type'] as String,
|
||||
billClientId: json['billClientId'] as String,
|
||||
id: json['id'] as String,
|
||||
debitRef: json['debitRef'] as String,
|
||||
debitCurrency: json['debitCurrency'] as String,
|
||||
amount: json['amount'] as String,
|
||||
@@ -39,6 +40,7 @@ _FormData _$FormDataFromJson(Map<String, dynamic> json) => _FormData(
|
||||
Map<String, dynamic> _$FormDataToJson(_FormData instance) => <String, dynamic>{
|
||||
'type': instance.type,
|
||||
'billClientId': instance.billClientId,
|
||||
'id': instance.id,
|
||||
'debitRef': instance.debitRef,
|
||||
'debitCurrency': instance.debitCurrency,
|
||||
'amount': instance.amount,
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
class AppTheme {
|
||||
static const _primaryColor = Color(0xFFD4AF37); // Gold
|
||||
static const _primaryColor = Color(0xFF9D6711); // #9D6711
|
||||
static const _secondaryColor = Color(0xFF8B0000); // Dark Red
|
||||
static const _tertiaryColor = Color(0xFF000000); // Black
|
||||
static const _backgroundColor = Color(0xFFFFFBFE);
|
||||
@@ -56,19 +56,19 @@ class AppTheme {
|
||||
fillColor: _surfaceColor,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
borderSide: BorderSide(color: Colors.grey.shade300, width: 1),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
borderSide: BorderSide(color: Colors.grey.shade300, width: 1),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: _primaryColor),
|
||||
borderSide: const BorderSide(color: _primaryColor, width: 2),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: _errorColor),
|
||||
borderSide: const BorderSide(color: _errorColor, width: 1),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
),
|
||||
|
||||