Commit 0515ce45 authored by Sai Srinivas's avatar Sai Srinivas
Browse files

Razorpay setup

parents f3a137b2 f693e7f1
class TransactionListResponse {
String? error;
String? balanceAmount;
String? balanceType;
String? totalCredit;
String? totalDebit;
Map<String, List<TransactionItem>>? transactions;
String? message;
TransactionListResponse({
this.error,
this.balanceAmount,
this.balanceType,
this.totalCredit,
this.totalDebit,
this.transactions,
this.message,
});
TransactionListResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
balanceAmount = json['balance_amount'];
balanceType = json['balance_type'];
totalCredit = json['total_credit'];
totalDebit = json['total_debit'];
message = json['message'];
if (json['transactions'] != null) {
transactions = {};
json['transactions'].forEach((key, value) {
transactions![key] = List<TransactionItem>.from(
value.map((v) => TransactionItem.fromJson(v)),
);
});
}
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['error'] = error;
data['balance_amount'] = balanceAmount;
data['balance_type'] = balanceType;
data['total_credit'] = totalCredit;
data['total_debit'] = totalDebit;
data['message'] = message;
if (transactions != null) {
data['transactions'] = transactions!.map((key, value) =>
MapEntry(key, value.map((v) => v.toJson()).toList()));
}
return data;
}
}
class TransactionItem {
String? id;
String? billId;
String? atype;
String? type;
String? datetime;
String? cAmount;
String? dAmount;
String? narration;
TransactionItem({
this.id,
this.billId,
this.atype,
this.type,
this.datetime,
this.cAmount,
this.dAmount,
this.narration,
});
TransactionItem.fromJson(Map<String, dynamic> json) {
id = json['id'];
billId = json['bill_id'];
atype = json['atype'];
type = json['type'];
datetime = json['datetime'];
cAmount = json['c_amount'];
dAmount = json['d_amount'];
narration = json['narration'];
}
Map<String, dynamic> toJson() => {
'id': id,
'bill_id': billId,
'atype': atype,
'type': type,
'datetime': datetime,
'c_amount': cAmount,
'd_amount': dAmount,
'narration': narration,
};
}
// class TransactionListResponse {
// String? error;
// String? balanceAmount;
// String? balanceType;
// String? totalCredit;
// String? totalDebit;
// Map<String, List<TransactionItem>>? transactions;
// String? message;
//
// TransactionListResponse({
// this.error,
// this.balanceAmount,
// this.balanceType,
// this.totalCredit,
// this.totalDebit,
// this.transactions,
// this.message,
// });
//
// TransactionListResponse.fromJson(Map<String, dynamic> json) {
// error = json['error'];
// balanceAmount = json['balance_amount'];
// balanceType = json['balance_type'];
// totalCredit = json['total_credit'];
// totalDebit = json['total_debit'];
// message = json['message'];
//
// if (json['transactions'] != null) {
// transactions = {};
// json['transactions'].forEach((key, value) {
// transactions![key] = List<TransactionItem>.from(
// value.map((v) => TransactionItem.fromJson(v)),
// );
// });
// }
// }
//
// Map<String, dynamic> toJson() {
// final data = <String, dynamic>{};
// data['error'] = error;
// data['balance_amount'] = balanceAmount;
// data['balance_type'] = balanceType;
// data['total_credit'] = totalCredit;
// data['total_debit'] = totalDebit;
// data['message'] = message;
// if (transactions != null) {
// data['transactions'] = transactions!.map((key, value) =>
// MapEntry(key, value.map((v) => v.toJson()).toList()));
// }
// return data;
// }
// }
//
// class TransactionItem {
// String? id;
// String? billId;
// String? atype;
// String? type;
// String? datetime;
// String? cAmount;
// String? dAmount;
// String? narration;
//
// TransactionItem({
// this.id,
// this.billId,
// this.atype,
// this.type,
// this.datetime,
// this.cAmount,
// this.dAmount,
// this.narration,
// });
//
// TransactionItem.fromJson(Map<String, dynamic> json) {
// id = json['id'];
// billId = json['bill_id'];
// atype = json['atype'];
// type = json['type'];
// datetime = json['datetime'];
// cAmount = json['c_amount'];
// dAmount = json['d_amount'];
// narration = json['narration'];
// }
//
// Map<String, dynamic> toJson() => {
// 'id': id,
// 'bill_id': billId,
// 'atype': atype,
// 'type': type,
// 'datetime': datetime,
// 'c_amount': cAmount,
// 'd_amount': dAmount,
// 'narration': narration,
// };
// }
class PayAmountResponse {
String? error;
String? amount;
String? message;
String? orderId;
String? razorKey;
String? sessionExists;
PayAmountResponse(
{this.error,
this.amount,
this.message,
this.orderId,
this.razorKey,
this.sessionExists});
PayAmountResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
amount = json['amount'];
message = json['message'];
orderId = json['order_id'];
razorKey = json['razor_key'];
sessionExists = json['session_exists'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
data['amount'] = this.amount;
data['message'] = this.message;
data['order_id'] = this.orderId;
data['razor_key'] = this.razorKey;
data['session_exists'] = this.sessionExists;
return data;
}
}
......@@ -6,6 +6,7 @@ class TransactionListResponse {
String? totalDebit;
Map<String, List<TransactionItem>>? transactions;
String? message;
String? lastPaid;
TransactionListResponse({
this.error,
......@@ -24,7 +25,7 @@ class TransactionListResponse {
totalCredit = json['total_credit'];
totalDebit = json['total_debit'];
message = json['message'];
lastPaid = json['last_payment'];
if (json['transactions'] != null) {
transactions = {};
json['transactions'].forEach((key, value) {
......@@ -43,6 +44,7 @@ class TransactionListResponse {
data['total_credit'] = totalCredit;
data['total_debit'] = totalDebit;
data['message'] = message;
data['last_payment'] = lastPaid;
if (transactions != null) {
data['transactions'] = transactions!.map((key, value) =>
MapEntry(key, value.map((v) => v.toJson()).toList()));
......
......@@ -5,7 +5,7 @@ import 'package:gen_service/Services/api_calling.dart';
import '../Models/CommonResponse.dart';
import '../Models/HelpAndComplaintModels/ComplaintListResponse.dart';
import '../Models/HelpAndComplaintModels/GeneratorListResponse.dart';
import '../Screens/HelpAndComplaintScreens/DropDownsListResponse.dart';
import '../Models/HelpAndComplaintModels/DropDownsListResponse.dart';
class HelpAndComplaintProvider extends ChangeNotifier {
bool _isLoading = false;
......
import 'package:flutter/cupertino.dart';
import '../Models/TransactionModels/PayAmountResponse.dart';
import '../Services/api_calling.dart';
class PayAmountProvider with ChangeNotifier {
bool _isLoading = false;
PayAmountResponse? _payResponse;
PaymentStatusResponse? _statusResponse;
String? _errorMessage;
bool get isLoading => _isLoading;
PayAmountResponse? get payResponse => _payResponse;
PaymentStatusResponse? get statusResponse => _statusResponse;
String? get errorMessage => _errorMessage;
/// Pay Amount API
Future<void> payAmount({
required String sessionId,
required String empId,
required String amount,
required String refType,
required String refId,
}) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final res = await ApiCalling.payAmountApi(
sessionId,
empId,
amount,
refType,
refId,
);
if (res != null) {
_payResponse = res;
} else {
_errorMessage = "No response from server";
}
} catch (e) {
_errorMessage = "Error: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
/// Get Payment Status API
Future<void> getPaymentStatus({
required String sessionId,
required String empId,
required String razorpayOrderId,
}) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final res = await ApiCalling.getPaymentStatusApi(
sessionId,
empId,
razorpayOrderId,
);
if (res != null) {
_statusResponse = res;
} else {
_errorMessage = "No response from server";
}
} catch (e) {
_errorMessage = "Error: $e";
} finally {
_isLoading = false;
notifyListeners();
}
}
/// Reset all states
void reset() {
_payResponse = null;
_statusResponse = null;
_errorMessage = null;
notifyListeners();
}
}
// Payment Status Response
class PaymentStatusResponse {
int? error;
double? amount;
String? date;
String? message;
int? sessionExists;
PaymentStatusResponse(
{this.error, this.amount, this.date, this.message, this.sessionExists});
PaymentStatusResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
amount = json['amount'];
date = json['date'];
message = json['message'];
sessionExists = json['session_exists'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['error'] = this.error;
data['amount'] = this.amount;
data['date'] = this.date;
data['message'] = this.message;
data['session_exists'] = this.sessionExists;
return data;
}
}
\ No newline at end of file
......@@ -12,26 +12,40 @@ import '../Services/api_post_request.dart';
import '../Utility/CustomSnackbar.dart';
class TransactionsProvider with ChangeNotifier {
TransactionListResponse? _transactionList;
bool _isLoading = false;
String? _errorMessage;
TransactionListResponse? _transactionList;
int _currentPage = 1;
bool _hasMore = true;
bool _isLoadingMore = false;
TransactionListResponse? get transactionList => _transactionList;
bool get isLoading => _isLoading;
bool get isLoadingMore => _isLoadingMore;
String? get errorMessage => _errorMessage;
TransactionListResponse? get transactionList => _transactionList;
bool get hasMore => _hasMore;
/// Fetch Transactions from API
/// Fetch Transactions from API (initial load)
Future<void> fetchTransactions(String accId, String sessionId) async {
_currentPage = 1;
_hasMore = true;
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.fetchTransactionListApi(accId, sessionId);
final response = await ApiCalling.fetchTransactionListApi(
accId,
sessionId,
_currentPage.toString()
);
if (response != null) {
_transactionList = response;
_errorMessage = null;
// Check if we have more data (assuming 15 items per page)
_hasMore = _calculateHasMore(response);
} else {
_errorMessage = "No response from server";
}
......@@ -43,10 +57,86 @@ class TransactionsProvider with ChangeNotifier {
notifyListeners();
}
/// Load more transactions
Future<void> loadMoreTransactions(String accId, String sessionId) async {
if (_isLoadingMore || !_hasMore) return;
_isLoadingMore = true;
notifyListeners();
try {
final response = await ApiCalling.fetchTransactionListApi(
accId,
sessionId,
(_currentPage + 1).toString()
);
debugPrint("\nPage Number ${_currentPage+1}");
if (response != null && response.transactions != null) {
_currentPage++;
// Merge new transactions with existing ones
_mergeTransactions(response);
// Check if we have more data
_hasMore = _calculateHasMore(response);
}
} catch (e) {
debugPrint("Load more error: $e");
}
_isLoadingMore = false;
notifyListeners();
}
/// Merge new transactions with existing data
void _mergeTransactions(TransactionListResponse newResponse) {
if (_transactionList == null) {
_transactionList = newResponse;
return;
}
// Merge transactions by month
if (newResponse.transactions != null) {
if (_transactionList!.transactions == null) {
_transactionList!.transactions = {};
}
newResponse.transactions!.forEach((month, newItems) {
if (_transactionList!.transactions!.containsKey(month)) {
// Month exists, append items
_transactionList!.transactions![month]!.addAll(newItems);
} else {
// New month, add all items
_transactionList!.transactions![month] = newItems;
}
});
}
}
/// Check if there are more pages to load
bool _calculateHasMore(TransactionListResponse response) {
if (response.transactions == null || response.transactions!.isEmpty) {
return false;
}
// Count total items in current response
int totalItems = 0;
response.transactions!.forEach((month, items) {
totalItems += items.length;
});
// If we got less than 15 items, probably no more data
return totalItems >= 15;
}
/// Clear Data
void clearTransactions() {
_transactionList = null;
_errorMessage = null;
_currentPage = 1;
_hasMore = true;
_isLoadingMore = false;
notifyListeners();
}
......
......@@ -85,6 +85,7 @@ class _OtpScreenState extends State<OtpScreen> {
if (authProvider.otpResponse?.accId != null) {
await prefs.saveString("accId", authProvider.otpResponse!.accId!);
await prefs.saveString("session_id", authProvider.otpResponse!.sessionId!);
await prefs.saveString("mob_number", widget.mob);
}
// ✅ Navigate to Home Screen
......@@ -94,6 +95,7 @@ class _OtpScreenState extends State<OtpScreen> {
pageBuilder: (_, __, ___) => HomeScreen(
accId: authProvider.otpResponse!.accId!,
sessionId: authProvider.otpResponse!.sessionId.toString(),
mobNumber: widget.mob,
),
transitionsBuilder: (_, animation, __, child) {
return FadeTransition(opacity: animation, child: child);
......
......@@ -88,7 +88,7 @@ class _AddComplaintScreenState extends State<AddComplaintScreen> {
StretchMode.blurBackground,
],
background: Container(
decoration: const BoxDecoration(color: AppColors.primary),
decoration: const BoxDecoration(gradient: AppColors.commonAppBarGradient),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
......@@ -123,7 +123,7 @@ class _AddComplaintScreenState extends State<AddComplaintScreen> {
/// Main content
SliverToBoxAdapter(
child: Container(
color: AppColors.primary,
color: AppColors.backgroundBottom,
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
......
......@@ -114,7 +114,9 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
],
background: Container(
decoration:
const BoxDecoration(color: AppColors.primary),
const BoxDecoration(
gradient: AppColors.commonAppBarGradient
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Row(
......@@ -149,7 +151,7 @@ class _ComplaintListScreenState extends State<ComplaintListScreen> {
SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.only(top: 1),
color: AppColors.primary,
color: AppColors.backgroundBottom,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
decoration: const BoxDecoration(
......@@ -318,14 +320,14 @@ class ComplaintCard extends StatelessWidget {
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
boxShadow: [
BoxShadow(
color: Colors.grey.shade200,
blurRadius: 4,
offset: const Offset(0, 2),
)
],
// border: Border.all(color: Colors.grey.shade200),
// boxShadow: [
// BoxShadow(
// color: Colors.grey.shade200,
// blurRadius: 4,
// offset: const Offset(0, 2),
// )
// ],
),
child: Column(
children: [
......
......@@ -108,7 +108,7 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
StretchMode.blurBackground,
],
background: Container(
decoration: const BoxDecoration(color: AppColors.primary),
decoration: const BoxDecoration(gradient: AppColors.commonAppBarGradient),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
......@@ -145,7 +145,7 @@ class _SelectOrderHelpScreenState extends State<SelectOrderHelpScreen> {
/// Main content
SliverToBoxAdapter(
child: Container(
color: AppColors.primary,
color: AppColors.backgroundBottom,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
......
......@@ -9,8 +9,10 @@ import 'package:gen_service/Screens/ProfileScreen.dart';
import 'package:gen_service/Screens/TransactionScreens/TransactionListScreen.dart';
import 'package:provider/provider.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
import '../Notifiers/DashboardProvider.dart';
import '../Notifiers/PayAmountProvider.dart';
import '../Utility/AppColors.dart';
......@@ -20,11 +22,13 @@ import 'generatorDetailsScreen.dart';
class HomeScreen extends StatefulWidget {
final String accId;
final String sessionId;
final String mobNumber;
const HomeScreen({
Key? key,
required this.accId,
required this.sessionId,
required this.mobNumber,
}) : super(key: key);
@override
......@@ -33,6 +37,10 @@ class HomeScreen extends StatefulWidget {
class _HomeScreenState extends State<HomeScreen> {
late Razorpay _razorpay;
bool? isSuccess;
var paymentMethod = "";
var User_contact = "0";
bool _stretch = true;
DateTime? currentBackPressTime;
......@@ -40,12 +48,136 @@ class _HomeScreenState extends State<HomeScreen> {
@override
void initState() {
super.initState();
_razorpay = Razorpay();
Future.microtask(() {
final dashboardProvider =
Provider.of<DashboardProvider>(context, listen: false);
dashboardProvider.fetchDashboard(widget.accId, widget.sessionId);
});
}
//_________________________________________________________
void _handlePaymentSuccess(PaymentSuccessResponse response) {
setState(() async {
final provider = Provider.of<PayAmountProvider>(context, listen: false);
await provider.getPaymentStatus(
sessionId: widget.sessionId,
empId: widget.accId,
razorpayOrderId: response.orderId.toString()
);
final data = provider.statusResponse;
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => PaymentSuccessFaillScreen(
// total: "${data?.amount}",
// date: "${data?.date}",
// payMode: "UPI",
// status: "Success",
// )),
// );
_razorpay.clear();
CustomSnackBar.showSuccess(
context: context,
message: data?.message ?? "Payment Success!",
);
// buttonLoading = false;
});
}
void _handlePaymentError(PaymentFailureResponse response) {
setState(() async {
CustomSnackBar.showError(
context: context,
message: "Payment failed, please try again.",
);
});
_razorpay.clear();
CustomSnackBar.showError(
context: context,
message: "Payment failed, please try again.",
);
}
void _handleExternalWallet(ExternalWalletResponse response) {
_razorpay.clear();
}
Future<void> payAmountFunction(String amount) async {
try {
final provider = Provider.of<PayAmountProvider>(context, listen: false);
await provider.payAmount(
sessionId: widget.sessionId,
empId: widget.accId,
amount: amount,
refType: "Gen Service",
refId: "1",
);
final data = provider.payResponse;
if (data != null) {
if (data.error == "0") {
openCheckout(data.orderId, data.razorKey!);
} else {
CustomSnackBar.showError(
context: context,
message: "${data.message}",
);
debugPrint("❌ Could not Complete Payment: ${data.message}");
}
} else {
debugPrint("❌ No response received from PayAmount API");
}
} catch (e) {
debugPrint("❌ 'Error occurred: $e'");
}
}
//razorpay payments__________________________________________________________
void openCheckout(razorPayOrderId, String razorpayKey) async {
_razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
_razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
_razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
// _buildCheckWidget();
Map<String, dynamic> options = {
'key': razorpayKey,
'amount': int.parse("${((0) * 100).round()}"),
'name': 'Gen Service',
'order_id': razorPayOrderId,
'description': "Bill",
'currency': 'INR',
'method': 'upi',
'prefill': {'contact': widget.mobNumber, 'email': ''}
};
// print(options);
try {
_razorpay.open(options);
} catch (e, s) {
// FirebaseCrashlytics.instance.log('Error occurred: $e');
// FirebaseCrashlytics.instance.recordError(e, s);
debugPrint(e.toString());
}
}
void verifyPayment(String orderId) {
isSuccess = true;
setState(() {
// toast(context, "Order Placed Successfully");
// print("Verify Payment");
});
_razorpay.clear();
}
// void onError(CFErrorResponse errorResponse, String orderId) {
// isSuccess = false;
// setState(() {
// // print(errorResponse.getMessage());
// // print("Error while making payment");
// });
// }
Future<bool> _onWillPop() async {
......@@ -200,8 +332,8 @@ class _HomeScreenState extends State<HomeScreen> {
data.userName.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w600,
fontSize: 24,
fontWeight: FontWeight.w400,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
......@@ -211,8 +343,9 @@ class _HomeScreenState extends State<HomeScreen> {
Text(
'+91 ${data.mobNum}',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.white.withOpacity(0.9),
fontSize: 14,
fontSize: 16,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
......@@ -288,7 +421,7 @@ class _HomeScreenState extends State<HomeScreen> {
Text('Facing Issues?',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w400,
color: AppColors.amountText)),
Text(
'Raise a ticket to resolve your issues.',
......@@ -318,7 +451,7 @@ class _HomeScreenState extends State<HomeScreen> {
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 22),
decoration: BoxDecoration(
color: AppColors.amountText,
borderRadius: const BorderRadius.only(
......@@ -339,7 +472,7 @@ class _HomeScreenState extends State<HomeScreen> {
'Get in touch With Us',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w400,
color: Colors.white),
),
const SizedBox(height: 4),
......@@ -353,7 +486,7 @@ class _HomeScreenState extends State<HomeScreen> {
],
),
),
const Icon(Icons.arrow_circle_right,
const Icon(Icons.arrow_circle_right_sharp,
color: Color(0xFFFFFFFF), size: 30),
],
),
......@@ -471,119 +604,116 @@ class _HomeScreenState extends State<HomeScreen> {
description,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.normalText,
height: 1.4,
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 14),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Status Badge with checkmark for AMC Protected
if (amc == "1" || amc == "2")
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
gradient: amc == "1"
? AppColors.greenStripGradient
: AppColors.fadeGradient,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
SvgPicture.asset(
"assets/svg/tick_ic.svg",
height: 14,
Row(
children: [
// Status Badge with checkmark for AMC Protected
if (amc == "1" || amc == "2")
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
gradient: amc == "1"
? AppColors.greenStripGradient
: AppColors.fadeGradient,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
SvgPicture.asset(
"assets/svg/tick_ic.svg",
height: 14,
color: amc == "1"
? AppColors.greenICBg
: AppColors.subtitleText,
),
const SizedBox(width: 4),
Text(
"AMC ",
style: TextStyle(
fontSize: 11,
fontFamily: "PoppinsBold",
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w700,
color: amc == "1"
? AppColors.greenICBg
: AppColors.subtitleText,
),
const SizedBox(width: 4),
Text(
"AMC ",
style: TextStyle(
fontSize: 11,
fontFamily: "PoppinsBold",
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w700,
color: amc == "1"
? AppColors.greenICBg
: AppColors.subtitleText,
),
),
Text(
"Protected",
style: TextStyle(
fontSize: 11,
fontFamily: "PoppinsBold",
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w700,
color: amc == "1"
? AppColors.normalText
: AppColors.subtitleText,
),
),
Text(
"Protected",
style: TextStyle(
fontSize: 11,
fontFamily: "PoppinsBold",
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w700,
color: amc == "1"
? AppColors.normalText
: AppColors.subtitleText,
),
SizedBox(width: 4,),
if (amc == "2")
const Icon(Icons.info_outline, color: Colors.red, size: 12,)
],
),
],
),
),
SizedBox(width: 4,),
if (amc == "2")
const Icon(Icons.info_outline, color: Colors.red, size: 12,)
],
),
],
),
// for warranty
if (warranty == "1" || warranty == "2")
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
gradient: warranty == "1"
? AppColors.yellowStripGradient
: AppColors.fadeGradient,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
SvgPicture.asset(
"assets/svg/tick2_ic.svg",
height: 14,
),
if (amc == "1" || amc == "2")
SizedBox(width: 16,),
// for warranty
if (warranty == "1" || warranty == "2")
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
gradient: warranty == "1"
? AppColors.yellowStripGradient
: AppColors.fadeGradient,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
SvgPicture.asset(
"assets/svg/tick2_ic.svg",
height: 14,
color: warranty == "1"
? AppColors.warning
: AppColors.subtitleText,
),
const SizedBox(width: 6),
Text(
" Warranty",
style: TextStyle(
fontSize: 11,
fontFamily: "PoppinsBold",
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w700,
color: warranty == "1"
? AppColors.warning
? AppColors.normalText
: AppColors.subtitleText,
),
const SizedBox(width: 6),
Text(
"Warranty",
style: TextStyle(
fontSize: 11,
fontFamily: "PoppinsBold",
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w700,
color: warranty == "1"
? AppColors.normalText
: AppColors.subtitleText,
),
),
SizedBox(width: 6,),
if (warranty == "2")
const Icon(Icons.info_outline, color: Colors.red, size: 12,)
],
),
],
),
),
SizedBox(width: 6,),
if (warranty == "2")
const Icon(Icons.info_outline, color: Colors.red, size: 12,)
],
),
],
),
],
),
),
],
)
],
......@@ -633,9 +763,9 @@ class _HomeScreenState extends State<HomeScreen> {
Text(
date,
style: TextStyle(
fontSize: 12,
fontSize: 16,
color: AppColors.normalText,
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w400,
),
),
Text(
......@@ -643,7 +773,7 @@ class _HomeScreenState extends State<HomeScreen> {
style: TextStyle(
fontSize: 12,
color: AppColors.subtitleText,
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w400,
),
),
],
......@@ -670,7 +800,7 @@ class _HomeScreenState extends State<HomeScreen> {
const Text(
'Transactions',
style: TextStyle(
fontSize: 16,
fontSize: 15,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
......@@ -695,7 +825,7 @@ class _HomeScreenState extends State<HomeScreen> {
const SizedBox(height: 8),
Container(
width: double.infinity,
padding: const EdgeInsets.all(22),
padding: EdgeInsets.all(22),
decoration: BoxDecoration(
gradient: dashboardData.balanceType == 'Pending Balance'? AppColors.balanceCardGradientP : AppColors.balanceCardGradientA,
borderRadius: BorderRadius.circular(16),
......@@ -703,84 +833,90 @@ class _HomeScreenState extends State<HomeScreen> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"₹",
style: TextStyle(
color: Colors.white,
fontSize: 20,
height: 2,
fontWeight: FontWeight.w500,
Expanded(
flex: 6,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"₹",
style: TextStyle(
color: Colors.white,
fontSize: 20,
height: 2,
fontWeight: FontWeight.w500,
),
),
),
Text(
dashboardData?.balanceAmount?.toString() ?? "0",
style: TextStyle(
color: Colors.white,
fontSize: 34,
fontWeight: FontWeight.w500,
Text(
dashboardData?.balanceAmount?.toString() ?? "0",
style: TextStyle(
color: Colors.white,
fontSize: 34,
fontWeight: FontWeight.w500,
),
),
),
],
),
const SizedBox(height: 4),
Row(
children: [
const Icon(Icons.info_outline,
color: Colors.white, size: 16),
const SizedBox(width: 6),
Text(
dashboardData.balanceType ?? 'Pending Balance',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
],
),
const SizedBox(height: 4),
Row(
children: [
const Icon(Icons.info_outline,
color: Colors.white, size: 16),
const SizedBox(width: 6),
Text(
dashboardData.balanceType ?? '',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
fontSize: 12,
),
),
),
],
),
],
),
Column(
children: [
Text(
"*Make sure to pay before\n you incur any fines.",
maxLines: 2,
style: TextStyle(
color: const Color(0xAAFFFFFF),
fontSize: 12,
fontWeight: FontWeight.w500,
],
),
),
const SizedBox(height: 10),
if (dashboardData.balanceType == 'Pending Balance')
InkResponse(
onTap: () {
// Add pay now logic
},
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
],
),
),
Expanded(
flex: 6,
child: Column(
children: [
Text(
"*Make sure to pay before\n you incur any fines.",
maxLines: 2,
textAlign: TextAlign.end,
style: TextStyle(
color: const Color(0xAAFFFFFF),
fontSize: 12,
fontWeight: FontWeight.w400,
),
child: const Text(
" Pay Now ",
style: TextStyle(
color: Colors.blue,
fontFamily: "Poppins",
fontSize: 14,
fontWeight: FontWeight.w500,
),
const SizedBox(height: 10),
if (dashboardData.balanceType == 'Pending Balance')
InkResponse(
onTap: ()=> _openPaymentSheet(context, dashboardData?.balanceAmount!),
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 23, vertical: 7),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: const Text(
" Pay Now ",
style: TextStyle(
color: Colors.blue,
fontFamily: "Poppins",
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
),
),
],
],
),
),
],
),
......@@ -873,7 +1009,7 @@ class _HomeScreenState extends State<HomeScreen> {
"#${c.hashId ?? ''} | ${c.complaintName ?? ''}",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.amountText,
),
),
......@@ -882,7 +1018,8 @@ class _HomeScreenState extends State<HomeScreen> {
c.registredDate ?? '',
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText,
),
),
],
......@@ -891,7 +1028,7 @@ class _HomeScreenState extends State<HomeScreen> {
// Status badge
Container(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8),
horizontal: 16, vertical: 6),
decoration: BoxDecoration(
color: (c.openStatus?.toLowerCase() == 'open')
? AppColors.successBG
......@@ -901,8 +1038,8 @@ class _HomeScreenState extends State<HomeScreen> {
child: Text(
c.openStatus ?? '',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontSize: 14,
fontWeight: FontWeight.w400,
color: (c.openStatus?.toLowerCase() == 'open')
? AppColors.success
: AppColors.warningText,
......@@ -923,8 +1060,8 @@ class _HomeScreenState extends State<HomeScreen> {
child: Text(
c.productName ?? "Unknown Product",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
fontSize: 12,
fontWeight: FontWeight.w400,
color: AppColors.normalText,
),
),
......@@ -936,7 +1073,7 @@ class _HomeScreenState extends State<HomeScreen> {
"#${c.id ?? ''} | ",
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText,
),
),
......@@ -961,4 +1098,248 @@ class _HomeScreenState extends State<HomeScreen> {
);
}
void _openPaymentSheet(BuildContext context, String totalAmountStr) {
TextEditingController amountController = TextEditingController();
bool isPartPayment = false;
final double totalAmount = double.tryParse(totalAmountStr) ?? 0;
showModalBottomSheet(
isScrollControlled: true,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
top: 16,
left: 16,
right: 16,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Handle Bar
Center(
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(4),
),
),
),
const SizedBox(height: 16),
// Title
const Text(
"Balance Amount Bill",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Poppins",
),
),
const SizedBox(height: 6),
Divider(),
const SizedBox(height: 10),
// Pay Total Option
GestureDetector(
onTap: () {
setState(() => isPartPayment = false);
},
child: Row(
children: [
Radio<bool>(
value: false,
groupValue: isPartPayment,
onChanged: (v) => setState(() => isPartPayment = v!),
activeColor: const Color(0xFF008CDE),
),
// Radio<bool>(
// value: false,
// groupValue: isPartPayment,
// onChanged: (v) => setState(() => isPartPayment = v!),
// ),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Pay Total",
style: TextStyle(
fontSize: 14,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
),
),
Text(
"Avoid late payment fees.",
style: TextStyle(
color: Color(0xff5FBB54),
fontSize: 12,
fontFamily: "Poppins",
),
),
],
),
const Spacer(),
Text(
"₹${totalAmount.toStringAsFixed(0)}",
style: const TextStyle(
fontSize: 14,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
),
),
],
),
),
const SizedBox(height: 10),
// Part Payment Option
GestureDetector(
onTap: () {
setState(() => isPartPayment = true);
},
child: Row(
children: [
Radio<bool>(
value: true,
groupValue: isPartPayment,
onChanged: (v) => setState(() => isPartPayment = v!),
activeColor: const Color(0xFF008CDE),
),
const Text(
"Part Payment",
style: TextStyle(
fontSize: 14,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
),
),
const SizedBox(width: 24),
Expanded(
child: Container(
height: 50,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
),
child: TextFormField(
controller: amountController,
enabled: isPartPayment,
style: const TextStyle(
fontSize: 14,
fontFamily: "Poppins",
color: Colors.black87,
),
keyboardType: TextInputType.number,
decoration: const InputDecoration(
hintText: "Enter amount",
hintStyle: TextStyle(
fontSize: 14,
fontFamily: "Poppins",
color: Colors.grey,
),
border: InputBorder.none,
),
),
),
),
],
),
),
const SizedBox(height: 6),
Divider(),
const SizedBox(height: 6),
// Continue Button
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
double enteredAmount = isPartPayment
? double.tryParse(amountController.text) ?? 0
: totalAmount;
if (enteredAmount <= 0) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Please enter a valid amount"),
),
);
return;
}
if (isPartPayment && enteredAmount > totalAmount) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
"Entered amount cannot exceed total amount",
),
),
);
return;
}
Navigator.pop(context);
// Pass selected amount to your payAmountFunction
payAmountFunction(enteredAmount.toStringAsFixed(2));
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF008CDE),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 22),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"Continue Payment",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.white,
fontSize: 16,
),
),
SvgPicture.asset(
"assets/svg/continue_ic.svg",
color: Colors.white,
height: 25,
width: 25,
),
],
),
),
),
),
const SizedBox(height: 16),
],
),
);
},
);
},
);
}
}
\ No newline at end of file
......@@ -356,7 +356,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
profileProvider.fetchProfile(widget.accId, widget.sessionId);
},
stretchTriggerOffset: 300.0,
expandedHeight: 245.0,
expandedHeight: 260.0,
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final top = constraints.biggest.height;
......@@ -418,8 +418,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
data.name.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w600,
fontSize: 24,
fontWeight: FontWeight.w400,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
......@@ -429,8 +429,9 @@ class _ProfileScreenState extends State<ProfileScreen> {
Text(
'+91 ${data.mobNum}',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.white.withOpacity(0.9),
fontSize: 14,
fontSize: 16,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
......@@ -449,7 +450,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
" Logout ",
style: TextStyle(
color: AppColors.normalText,
fontWeight: FontWeight.w400,
fontWeight: FontWeight.w600,
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
......@@ -582,7 +583,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
padding: EdgeInsets.all(16),
child: Row(
children: [
if(title != "State" && title != "Sub Locality")
Container(
padding: EdgeInsets.all(title =="Address" ? 8 :12),
decoration: BoxDecoration(
......@@ -595,20 +596,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
fit: BoxFit.contain,
),
),
if(title != "Email ID" && title != "Address")
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.all(14),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(18)
),
child: SizedBox(
width: 24,
height: 24,
),
),
),
SizedBox(width: 14,),
Expanded(
flex: 7,
......
......@@ -117,6 +117,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
Future<void> _navigateToDashboard() async {
final String? savedAccId = await prefs.getString("accId");
final String? savedSessionId = await prefs.getString("session_id");
final String? mobNumber = await prefs.getString("mob_number");
if (savedAccId != null && savedAccId.isNotEmpty) {
Navigator.pushReplacement(
......@@ -125,6 +126,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
pageBuilder: (_, __, ___) => HomeScreen(
accId: savedAccId,
sessionId: savedSessionId.toString(),
mobNumber: mobNumber.toString(),
),
transitionsBuilder: (_, animation, __, child) {
return FadeTransition(
......@@ -513,7 +515,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
),
const SizedBox(height: 4),
Text(
"Avantect Web Grid",
"Avantech Web Grid",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
......
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:gen_service/Notifiers/TransactionsProvider.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
import '../../Notifiers/PayAmountProvider.dart';
import '../../Utility/AppColors.dart';
import '../../Utility/CustomSnackbar.dart';
import '../../Utility/SharedpreferencesService.dart';
class BillDetailScreen extends StatefulWidget {
final String sessionId;
......@@ -21,15 +24,159 @@ class BillDetailScreen extends StatefulWidget {
}
class _BillDetailScreenState extends State<BillDetailScreen> {
late Razorpay _razorpay;
bool? isSuccess;
var paymentMethod = "";
var User_contact = "0";
final prefs = SharedPreferencesService.instance;
@override
void initState() {
super.initState();
_razorpay =Razorpay();
Future.microtask(() {
final provider = Provider.of<TransactionsProvider>(context, listen: false);
provider.fetchBillDetails(widget.accId, widget.sessionId, widget.billId);
});
}
//_________________________________________________________
void _handlePaymentSuccess(PaymentSuccessResponse response) {
setState(() async {
final provider = Provider.of<PayAmountProvider>(context, listen: false);
await provider.getPaymentStatus(
sessionId: widget.sessionId,
empId: widget.accId,
razorpayOrderId: response.orderId.toString()
);
final data = provider.statusResponse;
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => PaymentSuccessFaillScreen(
// total: "${data?.amount}",
// date: "${data?.date}",
// payMode: "UPI",
// status: "Success",
// )),
// );
_razorpay.clear();
CustomSnackBar.showSuccess(
context: context,
message: data?.message ?? "Payment Success!",
);
// buttonLoading = false;
});
}
void _handlePaymentError(PaymentFailureResponse response) {
setState(() async {
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => PaymentSuccessFaillScreen(
// total: "${data?.amount}",
// date: "${data?.date}",
// payMode: "UPI",
// status: "Fail",
// )),
// );
CustomSnackBar.showError(
context: context,
message: "Payment failed, please try again.",
);
});
_razorpay.clear();
CustomSnackBar.showError(
context: context,
message: "Payment failed, please try again.",
);
}
void _handleExternalWallet(ExternalWalletResponse response) {
_razorpay.clear();
}
Future<void> payAmountFunction(String amount, String id) async {
try {
final provider = Provider.of<PayAmountProvider>(context, listen: false);
await provider.payAmount(
sessionId: widget.sessionId,
empId: widget.accId,
amount: amount,
refType: "Payment",
refId: id,
);
final data = provider.payResponse;
if (data != null) {
if (data.error == "0") {
openCheckout(data.orderId, data.razorKey!);
} else {
CustomSnackBar.showError(
context: context,
message: "${data.message}",
);
debugPrint("❌ Could not Complete Payment: ${data.message}");
}
} else {
debugPrint("❌ No response received from PayAmount API");
}
} catch (e) {
debugPrint("❌ 'Error occurred: $e'");
}
}
//razorpay payments__________________________________________________________
void openCheckout(razorPayOrderId, String razorpayKey) async {
final String? mobNumber = await prefs.getString("mob_number");
_razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
_razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
_razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
// _buildCheckWidget();
Map<String, dynamic> options = {
'key': razorpayKey,
'amount': int.parse("${((0) * 100).round()}"),
'name': 'Gen Service',
'order_id': razorPayOrderId,
'description': "Bill",
'currency': 'INR',
'method': 'upi',
'prefill': {'contact': mobNumber, 'email': ''}
};
// print(options);
try {
_razorpay.open(options);
} catch (e, s) {
// FirebaseCrashlytics.instance.log('Error occurred: $e');
// FirebaseCrashlytics.instance.recordError(e, s);
debugPrint(e.toString());
}
}
void verifyPayment(String orderId) {
isSuccess = true;
setState(() {
// toast(context, "Order Placed Successfully");
// print("Verify Payment");
});
_razorpay.clear();
}
// void onError(CFErrorResponse errorResponse, String orderId) {
// isSuccess = false;
// setState(() {
// // print(errorResponse.getMessage());
// // print("Error while making payment");
// });
// }
@override
Widget build(BuildContext context) {
return Consumer<TransactionsProvider>(
......@@ -92,7 +239,7 @@ class _BillDetailScreenState extends State<BillDetailScreen> {
// Amount
Text(
"₹${details.totalAmount ?? "0"}",
"₹${details.totalAmount}",
style: const TextStyle(
fontSize: 32,
fontFamily: "Poppins",
......@@ -221,7 +368,17 @@ class _BillDetailScreenState extends State<BillDetailScreen> {
Expanded(
child: ElevatedButton(
onPressed: () {
// Razorpay or Payment
if(details.totalAmount == "0.00") {
CustomSnackBar.showWarning(
context: context,
message: "Invalid amount or less then 1",
);
}else{
Navigator.pop(context);
// handle payment navigation
payAmountFunction(details.totalAmount.toString(), details.id.toString());
}
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.amountText,
......
......@@ -3,8 +3,13 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:gen_service/Screens/TransactionScreens/BillDetailScreen.dart';
import 'package:gen_service/Screens/TransactionScreens/PaymentDetails.dart';
import 'package:provider/provider.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
import '../../Models/TransactionModels/TransactionListResponse.dart';
import '../../Notifiers/PayAmountProvider.dart';
import '../../Notifiers/TransactionsProvider.dart';
import '../../Utility/AppColors.dart';
import '../../Utility/CustomSnackbar.dart';
import '../../Utility/SharedpreferencesService.dart';
class TransactionListScreen extends StatefulWidget {
final String accId;
......@@ -22,14 +27,175 @@ class TransactionListScreen extends StatefulWidget {
class _TransactionScreenState extends State<TransactionListScreen> {
bool _stretch = true;
late Razorpay _razorpay;
bool? isSuccess;
var paymentMethod = "";
var User_contact = "0";
final ScrollController _scrollController = ScrollController();
final prefs = SharedPreferencesService.instance;
@override
void initState() {
super.initState();
_razorpay = Razorpay();
// Initial data load
Future.microtask(() {
Provider.of<TransactionsProvider>(context, listen: false)
.fetchTransactions(widget.accId, widget.sessionId);
});
// Setup scroll listener for pagination
_scrollController.addListener(_scrollListener);
}
//_________________________________________________________
void _handlePaymentSuccess(PaymentSuccessResponse response) {
setState(() async {
final provider = Provider.of<PayAmountProvider>(context, listen: false);
await provider.getPaymentStatus(
sessionId: widget.sessionId,
empId: widget.accId,
razorpayOrderId: response.orderId.toString()
);
final data = provider.statusResponse;
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => PaymentSuccessFaillScreen(
// total: "${data?.amount}",
// date: "${data?.date}",
// payMode: "UPI",
// status: "Success",
// )),
// );
_razorpay.clear();
CustomSnackBar.showSuccess(
context: context,
message: data?.message ?? "Payment Success!",
);
// buttonLoading = false;
});
}
void _handlePaymentError(PaymentFailureResponse response) {
setState(() async {
CustomSnackBar.showError(
context: context,
message: "Payment failed, please try again.",
);
});
_razorpay.clear();
CustomSnackBar.showError(
context: context,
message: "Payment failed, please try again.",
);
}
void _handleExternalWallet(ExternalWalletResponse response) {
_razorpay.clear();
}
Future<void> payAmountFunction(String amount) async {
try {
final provider = Provider.of<PayAmountProvider>(context, listen: false);
await provider.payAmount(
sessionId: widget.sessionId,
empId: widget.accId,
amount: amount,
refType: "Payment",
refId: "1",
);
final data = provider.payResponse;
if (data != null) {
if (data.error == "0") {
openCheckout(data.orderId, data.razorKey!);
} else {
CustomSnackBar.showError(
context: context,
message: "${data.message}",
);
debugPrint("❌ Could not Complete Payment: ${data.message}");
}
} else {
debugPrint("❌ No response received from PayAmount API");
}
} catch (e) {
debugPrint("❌ 'Error occurred: $e'");
}
}
//razorpay payments__________________________________________________________
void openCheckout(razorPayOrderId, String razorpayKey) async {
final String? mobNumber = await prefs.getString("mob_number");
_razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
_razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
_razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
// _buildCheckWidget();
Map<String, dynamic> options = {
'key': razorpayKey,
'amount': int.parse("${((0) * 100).round()}"),
'name': 'Gen Service',
'order_id': razorPayOrderId,
'description': "Bill",
'currency': 'INR',
'method': 'upi',
'prefill': {'contact': mobNumber, 'email': ''}
};
// print(options);
try {
_razorpay.open(options);
} catch (e, s) {
// FirebaseCrashlytics.instance.log('Error occurred: $e');
// FirebaseCrashlytics.instance.recordError(e, s);
debugPrint(e.toString());
}
}
void verifyPayment(String orderId) {
isSuccess = true;
setState(() {
// toast(context, "Order Placed Successfully");
// print("Verify Payment");
});
_razorpay.clear();
}
// void onError(CFErrorResponse errorResponse, String orderId) {
// isSuccess = false;
// setState(() {
// // print(errorResponse.getMessage());
// // print("Error while making payment");
// });
// }
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _scrollListener() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent - 200) {
_loadMore();
}
}
void _loadMore() {
final provider = Provider.of<TransactionsProvider>(context, listen: false);
if (!provider.isLoadingMore && provider.hasMore) {
provider.loadMoreTransactions(widget.accId, widget.sessionId);
}
}
@override
......@@ -38,11 +204,11 @@ class _TransactionScreenState extends State<TransactionListScreen> {
final balance = provider.transactionList?.balanceAmount ?? "0";
final balanceType = provider.transactionList?.balanceType ?? "Pending";
final lastPaid = provider.transactionList?.lastPaid;
final totalCredit = provider.transactionList?.totalCredit ?? "0";
final totalDebit = provider.transactionList?.totalDebit ?? "0";
final transactions = provider.transactionList?.transactions ?? {};
bool isPending = balanceType.toLowerCase() =="pending balance";
bool isPending = balanceType.toLowerCase() == "pending balance";
return RefreshIndicator.adaptive(
color: AppColors.amountText,
......@@ -51,80 +217,144 @@ class _TransactionScreenState extends State<TransactionListScreen> {
Provider.of<TransactionsProvider>(context, listen: false)
.fetchTransactions(widget.accId, widget.sessionId);
},
child: Scaffold(
backgroundColor: AppColors.backgroundRegular,
body: CustomScrollView(
controller: _scrollController, // Add controller here
physics: const BouncingScrollPhysics(),
slivers: [
/// Top SliverAppBar (balance card)
/// Top SliverAppBar (balance card) - No changes
SliverAppBar(
stretch: _stretch,
pinned: true,
expandedHeight: 245,
backgroundColor: AppColors.errorBg,
elevation: 0, // Remove shadow
leading: Container(), // Remove back button
toolbarHeight: 0, // Remove toolbar space
collapsedHeight: 0, // Completely collapse to 0 height
expandedHeight: isPending ? 252 : 210,
backgroundColor: isPending ? AppColors.errorBg : Color(0xFF4076FF),
elevation: 0,
leading: Container(),
toolbarHeight: 0,
collapsedHeight: 0,
flexibleSpace: FlexibleSpaceBar(
stretchModes: const [StretchMode.zoomBackground],
background: Container(
decoration: BoxDecoration(
gradient: isPending ? AppColors.balanceBarGradientP : AppColors.balanceBarGradientA,
gradient: isPending
? AppColors.balanceBarGradientP
: AppColors.balanceBarGradientA,
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 20),
const Text(
"Transactions",
style: TextStyle(
color: Colors.white,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
Text(
"₹$balance",
style: const TextStyle(
color: Colors.white,
fontSize: 36,
fontWeight: FontWeight.w600,
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
SizedBox(width: 8,),
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
color: Colors.white,
"assets/svg/continue_left_ic.svg",
height: 30,
),
),
),
Text(
"$balanceType",
style: const TextStyle(color: Colors.white70, fontSize: 16),
),
const SizedBox(height: 10),
if (isPending)
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.deepOrange,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
const SizedBox(width: 10),
const Text(
"Transactions",
style: TextStyle(
fontSize: 16,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
child: const Text("Pay Now"),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 18),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 20),
Text(
"₹$balance",
style: const TextStyle(
color: Colors.white,
fontSize: 34,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 2),
Text(
"$balanceType",
style: TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w400),
),
if(isPending == false)
const SizedBox(height: 4),
if(isPending == false)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.check_circle,
color: Colors.green,
size: 20,
),
SizedBox(width: 2,),
Text(
"Last Paid on $lastPaid.",
maxLines: 1,
style: TextStyle(
color: const Color(0xFeFFFFFF),
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
],
),
const SizedBox(height: 10),
if (isPending)
ElevatedButton(
onPressed: () =>_openPaymentSheet(context, balance),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.deepOrange,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: Text(
"Pay Now",
style: TextStyle(
fontSize: 14,
fontFamily: "Poppins",
fontWeight: FontWeight.w500,
color: AppColors.buttonColor,
),
),
),
SizedBox(height: 8,),
if (isPending)
Text(
"*Make sure to pay before you incur any fines.",
maxLines: 1,
style: TextStyle(
color: const Color(0xAAFFFFFF),
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
],
),
],
),
),
],
),
),
),
),
),
/// Summary Row (Paid / Pending)
/// Summary Row and Transaction List
SliverToBoxAdapter(
child: Container(
color: isPending ? AppColors.errorBg : Color(0xFF4076FF),
......@@ -139,7 +369,7 @@ class _TransactionScreenState extends State<TransactionListScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 4,),
const SizedBox(height: 4),
// Summary Card
Container(
margin: const EdgeInsets.all(16),
......@@ -159,89 +389,139 @@ class _TransactionScreenState extends State<TransactionListScreen> {
),
),
// Transaction List
ListView.builder(
shrinkWrap: true, // important for embedding inside SliverToBoxAdapter
physics: const NeverScrollableScrollPhysics(), // disable inner scrolling
itemCount: transactions.length,
itemBuilder: (context, index) {
final month = transactions.keys.elementAt(index);
final items = transactions[month]!;
return Container(
color: AppColors.backgroundRegular,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
month,
style: const TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 8),
...items.map((item) {
final type = item.atype ?? "Debit";
final title = item.narration ?? "No details";
final date = item.datetime ?? "";
final id = item.id ?? "";
final amount = type.toLowerCase() == "credit"
? "₹${item.cAmount ?? "0"}"
: "₹${item.dAmount ?? "0"}";
return InkResponse(
onTap: () {
// for credit
if (item.atype == "Credit") {
showDialog(
context: context,
builder: (context) => PaymentdetailDialog(
sessionId: widget.sessionId,
accId: widget.accId,
billId: item.billId.toString(),
),
);
} else {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => BillDetailScreen(
sessionId: widget.sessionId,
accId: widget.accId,
billId: item.billId.toString())
)
);
}
},
child: _buildTransactionItem(
type: type,
title: title,
id: id,
date: date,
amount: amount,
),
);
}).toList(),
],
),
);
},
),
_buildTransactionList(provider, transactions),
],
),
),
),
)
),
/// Loading indicator for pagination
if (provider.isLoadingMore)
const SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Center(
child: CircularProgressIndicator(color: AppColors.buttonColor),
),
),
),
/// No more data indicator
if (!provider.hasMore && transactions.isNotEmpty)
const SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Center(
child: Text(
"No more transactions",
style: TextStyle(
color: Colors.grey,
fontSize: 14,
),
),
),
),
),
],
),
),
);
}
Widget _buildTransactionList(TransactionsProvider provider, Map<String, List<TransactionItem>> transactions) {
if (provider.isLoading && transactions.isEmpty) {
return const Padding(
padding: EdgeInsets.all(16.0),
child: Center(child: CircularProgressIndicator(color: AppColors.buttonColor)),
);
}
if (transactions.isEmpty && !provider.isLoading) {
return const Padding(
padding: EdgeInsets.all(16.0),
child: Center(
child: Text(
"No transactions found",
style: TextStyle(color: Colors.grey, fontSize: 16),
),
),
);
}
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: transactions.length,
itemBuilder: (context, index) {
final month = transactions.keys.elementAt(index);
final items = transactions[month]!;
return Container(
color: AppColors.backgroundRegular,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
month,
style: const TextStyle(
fontFamily: "Poppins",
fontWeight: FontWeight.w500,
fontSize: 14,
),
),
const SizedBox(height: 8),
...items.map((item) {
final type = item.atype ?? "Debit";
final title = item.narration ?? "No details";
final date = item.datetime ?? "";
final id = item.id ?? "";
final amount = type.toLowerCase() == "credit"
? "₹${item.cAmount ?? "0"}"
: "₹${item.dAmount ?? "0"}";
return InkResponse(
onTap: () {
if (item.atype == "Credit") {
showDialog(
context: context,
builder: (context) => PaymentdetailDialog(
sessionId: widget.sessionId,
accId: widget.accId,
billId: item.billId.toString(),
),
);
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BillDetailScreen(
sessionId: widget.sessionId,
accId: widget.accId,
billId: item.billId.toString(),
),
),
);
}
},
child: _buildTransactionItem(
type: type,
title: title,
id: id,
date: date,
amount: amount,
),
);
}).toList(),
],
),
);
},
);
}
/// Summary Card Item
Widget _buildSummaryItem(String value, String label, Color color) {
return Row(
......@@ -293,7 +573,7 @@ class _TransactionScreenState extends State<TransactionListScreen> {
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
borderRadius: BorderRadius.circular(20),
),
child: Row(
children: [
......@@ -344,7 +624,7 @@ class _TransactionScreenState extends State<TransactionListScreen> {
style: const TextStyle(
fontFamily: "Poppins",
fontSize: 14,
fontWeight: FontWeight.normal,
fontWeight: FontWeight.w400,
color: Colors.black,
),
),
......@@ -366,4 +646,248 @@ class _TransactionScreenState extends State<TransactionListScreen> {
),
);
}
// payment sheet
void _openPaymentSheet(BuildContext context, String totalAmountStr) {
TextEditingController amountController = TextEditingController();
bool isPartPayment = false;
final double totalAmount = double.tryParse(totalAmountStr) ?? 0;
showModalBottomSheet(
isScrollControlled: true,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
top: 16,
left: 16,
right: 16,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Handle Bar
Center(
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(4),
),
),
),
const SizedBox(height: 16),
// Title
const Text(
"Balance Amount Bill",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Poppins",
),
),
const SizedBox(height: 6),
Divider(),
const SizedBox(height: 10),
// Pay Total Option
GestureDetector(
onTap: () {
setState(() => isPartPayment = false);
},
child: Row(
children: [
Radio<bool>(
value: false,
groupValue: isPartPayment,
onChanged: (v) => setState(() => isPartPayment = v!),
activeColor: const Color(0xFF008CDE),
),
// Radio<bool>(
// value: false,
// groupValue: isPartPayment,
// onChanged: (v) => setState(() => isPartPayment = v!),
// ),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Pay Total",
style: TextStyle(
fontSize: 14,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
),
),
Text(
"Avoid late payment fees.",
style: TextStyle(
color: Color(0xff5FBB54),
fontSize: 12,
fontFamily: "Poppins",
),
),
],
),
const Spacer(),
Text(
"₹${totalAmount.toStringAsFixed(0)}",
style: const TextStyle(
fontSize: 14,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
),
),
],
),
),
const SizedBox(height: 10),
// Part Payment Option
GestureDetector(
onTap: () {
setState(() => isPartPayment = true);
},
child: Row(
children: [
Radio<bool>(
value: true,
groupValue: isPartPayment,
onChanged: (v) => setState(() => isPartPayment = v!),
activeColor: const Color(0xFF008CDE),
),
const Text(
"Part Payment",
style: TextStyle(
fontSize: 14,
fontFamily: "Poppins",
fontWeight: FontWeight.w400,
),
),
const SizedBox(width: 24),
Expanded(
child: Container(
height: 50,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
),
child: TextFormField(
controller: amountController,
enabled: isPartPayment,
style: const TextStyle(
fontSize: 14,
fontFamily: "Poppins",
color: Colors.black87,
),
keyboardType: TextInputType.number,
decoration: const InputDecoration(
hintText: "Enter amount",
hintStyle: TextStyle(
fontSize: 14,
fontFamily: "Poppins",
color: Colors.grey,
),
border: InputBorder.none,
),
),
),
),
],
),
),
const SizedBox(height: 6),
Divider(),
const SizedBox(height: 6),
// Continue Button
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
double enteredAmount = isPartPayment
? double.tryParse(amountController.text) ?? 0
: totalAmount;
if (enteredAmount <= 0) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Please enter a valid amount"),
),
);
return;
}
if (isPartPayment && enteredAmount > totalAmount) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
"Entered amount cannot exceed total amount",
),
),
);
return;
}
Navigator.pop(context);
// Pass selected amount to your payAmountFunction
payAmountFunction(enteredAmount.toStringAsFixed(2));
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF008CDE),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 22),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"Continue Payment",
style: TextStyle(
fontFamily: "Poppins",
color: Colors.white,
fontSize: 16,
),
),
SvgPicture.asset(
"assets/svg/continue_ic.svg",
color: Colors.white,
height: 25,
width: 25,
),
],
),
),
),
),
const SizedBox(height: 16),
],
),
);
},
);
},
);
}
}
\ No newline at end of file
......@@ -59,7 +59,7 @@ class _GeneratordetailsscreenState extends State<Generatordetailsscreen> {
final error = detailsProvider.errorMessage;
final data = detailsProvider.detailsResponse;
final genDetails = data!.genDetails??GenDetails();
final locDetails = data!.locationDetails??LocationDetails();
final locDetails = data.locationDetails??LocationDetails();
final quotationsList = data.quotations??[];
final scheduleList = data.schedule ?? [];
final amcQuotationsList = data.amcQuotations??[];
......
......@@ -24,6 +24,8 @@ const amcQuoteListUrl = "${baseUrl}all_amc_quotations";
const complaintListUrl = "${baseUrl}all_complaint_list";
const downloadBillUrl = "${baseUrl}download_bill";
const downloadRecieptUrl = "${baseUrl}download_reciept";
const payAmountUrl = "${baseUrl}pay_amount";
const getPaymentStatusUrl = "${baseUrl}get_payment_status";
/// Help and complaints
const addComplaintUrl = "${baseUrl}add_complaint";
......
......@@ -15,8 +15,10 @@ import '../Models/AuthResponse.dart';
import '../Models/CommonResponse.dart';
import '../Models/DashboardResponse.dart';
import '../Models/HelpAndComplaintModels/GeneratorListResponse.dart';
import '../Models/TransactionModels/PayAmountResponse.dart';
import '../Models/TransactionModels/TransactionListResponse.dart';
import '../Screens/HelpAndComplaintScreens/DropDownsListResponse.dart';
import '../Models/HelpAndComplaintModels/DropDownsListResponse.dart';
import '../Notifiers/PayAmountProvider.dart';
import 'api_URLs.dart';
import 'api_post_request.dart';
import 'package:http/http.dart' as http show MultipartFile;
......@@ -150,12 +152,14 @@ class ApiCalling {
static Future<TransactionListResponse?> fetchTransactionListApi(
String accId,
String sessionId,
String pageNumber,
) async {
debugPrint("###############################Transaction Api calling ");
try {
Map<String, String> data = {
"acc_id": accId,
"session_id": sessionId,
"page_number": pageNumber,
};
final res = await post(data, transactionsUrl, {});
......@@ -286,6 +290,66 @@ class ApiCalling {
}
}
/// pay_amount
static Future<PayAmountResponse?> payAmountApi(
String sessionId,
String empId,
String ammount,
String refType,
String refId,
) async {
try {
Map<String, String> data = {
"session_id": sessionId,
"acc_id": empId,
"amount": ammount,
"ref_type": refType,
"ref_id": refId,
};
final res = await post(data, payAmountUrl, {});
debugPrint("PayAmount Response ${res?.body}");
if (res != null) {
return PayAmountResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint("❌ API Error (payAmountApi): $e");
return null;
}
}
/// Fetch or get_payment_status
static Future<PaymentStatusResponse?> getPaymentStatusApi(
String sessionId,
String empId,
String razorpayOrderId,
) async {
try {
Map<String, String> data = {
"session_id": sessionId,
"emp_id": empId,
"razorpay_order_id": razorpayOrderId,
};
final res = await post(data, getPaymentStatusUrl, {});
if (res != null) {
return PaymentStatusResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint("❌ API Error (getPaymentStatusApi): $e");
return null;
}
}
//___________________________________Help and Complaints
......
......@@ -45,7 +45,7 @@ class AppColors {
static const Color backgroundLight = Color(0xFFFFFFFF);
static const Color backgroundDark = Color(0xFF111827);
static const Color backgroundRegular = Color(0xFFF2F2F2);
static const Color backgroundBottom = Color(0xFF4076FF);
//Button
static const Color buttonColor = Color(0xFF008CDE);
......@@ -72,7 +72,7 @@ class AppColors {
end: Alignment.bottomRight,
colors: [
AppColors.subtitleText,
Color(0xFFFFFFFF),
Color(0x00FFFFFF),
],
);
......@@ -147,4 +147,13 @@ class AppColors {
Color(0xFF4076FF),
],
);
static const LinearGradient commonAppBarGradient = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFF04Bfef),
Color(0xFF4076FF),
],
);
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment