import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:razorpay_flutter/razorpay_flutter.dart'; import '../../Notifier/BillProvider.dart'; import '../../Notifier/PayAmountProvider.dart'; import '../../Utility/AppColors.dart'; import '../../Utility/CustomSnackbar.dart'; import '../../Utility/Reusablewidgets.dart'; import '../../Utility/SharedpreferencesService.dart'; import '../TransactionScreens/PaymentSuccessfailScreen.dart'; class BillDetailScreen extends StatefulWidget { final String sessionId; final String accId; final String billId; const BillDetailScreen({ super.key, required this.sessionId, required this.accId, required this.billId, }); @override State createState() => _BillDetailScreenState(); } class _BillDetailScreenState extends State { late Razorpay _razorpay; bool? isSuccess; var paymentMethod = ""; var User_contact = "0"; final prefs = SharedPreferencesService.instance; // Responsive text size function double getResponsiveTextSize(BuildContext context, double baseSize) { final double width = MediaQuery.of(context).size.width; if (width < 360) { // Small phones return baseSize * 0.85; } else if (width < 400) { // Medium phones return baseSize; } else { // Large phones return baseSize * 1.1; } } // Responsive padding function double getResponsivePadding(BuildContext context) { final double width = MediaQuery.of(context).size.width; return width * 0.04; } // Responsive icon size function double getResponsiveIconSize(BuildContext context, double baseSize) { final double width = MediaQuery.of(context).size.width; if (width < 360) { return baseSize * 0.8; } else if (width < 400) { return baseSize; } else { return baseSize * 1.2; } } @override void initState() { super.initState(); _razorpay = Razorpay(); Future.microtask(() { final provider = Provider.of(context, listen: false); provider.fetchBillDetails(widget.sessionId, widget.accId, widget.billId); }); } void _handlePaymentSuccess(PaymentSuccessResponse response) { setState(() async { final provider = Provider.of(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 { final provider = Provider.of(context, listen: false); await provider.getPaymentStatus( sessionId: widget.sessionId, empId: widget.accId, razorpayOrderId: "" ); final data = provider.statusResponse; 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 payAmountFunction(String amount, String id) async { try { final provider = Provider.of(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: "Could not Complete Payment: ${data.message}", ); debugPrint("❌ Could not Complete Payment: ${data.message}"); } } else { debugPrint("❌ No response received from PayAmount API"); } } catch (e) { CustomSnackBar.showError( context: context, message: 'Error occurred: $e', ); _razorpay.clear(); } } //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 options = { 'key': razorpayKey, 'amount': int.parse("${((0) * 100).round()}"), 'name': 'Gen Rentals', 'order_id': razorPayOrderId, 'description': "Bill", 'currency': 'INR', 'method': 'upi', 'prefill': {'contact': mobNumber, 'email': ''} }; // print(options); try { _razorpay.open(options); } catch (e, s) { _razorpay.clear(); debugPrint(e.toString()); } } void verifyPayment(String orderId) { isSuccess = true; setState(() { // toast(context, "Order Placed Successfully"); // print("Verify Payment"); }); _razorpay.clear(); } @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; final screenHeight = MediaQuery.of(context).size.height; final isSmallScreen = screenWidth < 360; return Consumer( builder: (context, provider, _) { if (provider.isDetailsLoading) { return Scaffold( body: Center( child: CircularProgressIndicator(), ), ); } final details = provider.billDetails; if (details == null) { return Scaffold( body: Center( child: Text( provider.errorMessage ?? "No details found", style: TextStyle(fontSize: getResponsiveTextSize(context, 16)), ), ), ); } final bool isPaid = details.billPaid == "Yes"; return SafeArea( top: false, child: Scaffold( appBar: AppBar( automaticallyImplyLeading: false, backgroundColor: Colors.white, elevation: 0, title: Row( children: [ InkResponse( onTap: () => Navigator.pop(context, true), child: SvgPicture.asset( "assets/svg/continue_left_ic.svg", height: getResponsiveIconSize(context, 28), ), ), SizedBox(width: screenWidth * 0.025), Text( "Bill Details", style: TextStyle( fontSize: getResponsiveTextSize(context, 16), fontFamily: "Poppins", fontWeight: FontWeight.w500, color: Colors.black87, ), ), ], ), ), backgroundColor: Colors.white, body: SingleChildScrollView( padding: EdgeInsets.symmetric( horizontal: getResponsivePadding(context), vertical: screenHeight * 0.025, ), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Status Icon Container( height: screenHeight * 0.09, width: screenHeight * 0.09, decoration: BoxDecoration( color: isPaid ? const Color(0xFFDFF8E1) : const Color(0xFFFFEBEB), shape: BoxShape.circle, ), child: Icon( isPaid ? Icons.check_circle : Icons.access_time_filled, color: isPaid ? Colors.green : Colors.redAccent, size: isPaid ? getResponsiveIconSize(context, 50) : getResponsiveIconSize(context, 40), ), ), SizedBox(height: screenHeight * 0.02), // Status Title Text( isPaid ? "Bill Paid" : "Bill Pending", style: TextStyle( fontSize: getResponsiveTextSize(context, 24), fontFamily: "Poppins", fontStyle: FontStyle.normal, fontWeight: FontWeight.w400, ), ), SizedBox(height: screenHeight * 0.008), // Total Amount Text( "₹${details.totalPrice ?? "0"}", style: TextStyle( fontSize: getResponsiveTextSize(context, 34), fontFamily: "Poppins", fontWeight: FontWeight.w500, ), ), SizedBox(height: screenHeight * 0.03), Divider(color: Colors.grey.shade300), SizedBox(height: screenHeight * 0.012), // Payment Details Align( alignment: Alignment.centerLeft, child: Text( "Payment Details", style: TextStyle( fontSize: getResponsiveTextSize(context, 14), fontWeight: FontWeight.w500, color: Colors.black87, ), ), ), SizedBox(height: screenHeight * 0.012), _buildRow("Date", "${details.paidDate ?? ''}", highlight: true), SizedBox(height: screenHeight * 0.018), Divider(color: Colors.grey.shade300), SizedBox(height: screenHeight * 0.01), // Product Details Align( alignment: Alignment.centerLeft, child: Text( "Products", style: TextStyle( fontSize: getResponsiveTextSize(context, 14), fontFamily: "Plus Jakarta Sans", fontWeight: FontWeight.w600, color: Colors.black87, ), ), ), SizedBox(height: screenHeight * 0.01), _buildRow(details.product ?? "N/A", "₹${details.basicPrice ?? "0"}"), SizedBox(height: screenHeight * 0.003), Divider(color: Colors.grey.shade300), SizedBox(height: screenHeight * 0.01), _buildRow("Sub Total", "₹${details.basicPrice ?? "0"}"), _buildRow("Gross Amount", "₹${details.totalPrice ?? "0"}"), _buildRow("SGST", details.sgst ?? "0"), _buildRow("CGST", details.cgst ?? "0"), _buildRow("IGST", details.igst ?? "0"), Divider(height: screenHeight * 0.04), _buildRow( "Total", "₹${details.totalPrice ?? "0"}", isBold: true, ), SizedBox(height: screenHeight * 0.05), ], ), ), bottomNavigationBar: _buildBottomButtons(provider, isPaid, screenWidth, screenHeight, details.totalPrice ?? "0", details.id ?? ""), ), ); }, ); } /// Conditional Bottom Buttons based on payment status Widget _buildBottomButtons(BillProvider provider, bool isPaid, double screenWidth, double screenHeight, String totalPrice, String id ) { return Container( padding: EdgeInsets.symmetric( horizontal: getResponsivePadding(context), vertical: screenHeight * 0.02, ), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 10, offset: const Offset(0, -2), ), ], ), child: Row( children: [ // Pay Now Button (only if not paid) if (!isPaid) ...[ Expanded( child: ElevatedButton( onPressed: () { Navigator.pop(context); // handle payment navigation payAmountFunction(totalPrice, id); }, style: ElevatedButton.styleFrom( backgroundColor: AppColors.buttonColor, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(screenWidth * 0.03), ), padding: EdgeInsets.symmetric(vertical: screenHeight * 0.018), elevation: 0, ), child: Text( "Pay Now", style: TextStyle( fontSize: getResponsiveTextSize(context, 14), fontFamily: "Poppins", fontWeight: FontWeight.w600, ), ), ), ), SizedBox(width: screenWidth * 0.03), ], // Download Receipt Button Expanded( child: ElevatedButton.icon( onPressed: provider.isDownloading ? null : () { provider.downloadBill( context, widget.sessionId, widget.billId, widget.accId, ); }, icon: provider.isDownloading ? SizedBox( width: getResponsiveIconSize(context, 16), height: getResponsiveIconSize(context, 16), child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : Icon( Icons.download, size: getResponsiveIconSize(context, 18), color: Colors.white, ), label: Text( provider.isDownloading ? "Downloading..." : "Download Bill", style: TextStyle( fontSize: getResponsiveTextSize(context, 14), fontFamily: "Plus Jakarta Sans", fontWeight: FontWeight.w600, color: Colors.white, ), ), style: ElevatedButton.styleFrom( backgroundColor: AppColors.amountText, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(screenWidth * 0.028), ), padding: EdgeInsets.symmetric(vertical: screenHeight * 0.018), ), ), ), ], ), ); } /// Pay Now Dialog void _showPayNowDialog(double screenWidth, double screenHeight) { showDialog( context: context, builder: (context) { return Dialog( backgroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(screenWidth * 0.05), ), child: Container( padding: EdgeInsets.all(screenWidth * 0.05), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: getResponsiveIconSize(context, 60), height: getResponsiveIconSize(context, 60), decoration: BoxDecoration( color: AppColors.buttonColor.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( Icons.payment_rounded, color: AppColors.buttonColor, size: getResponsiveIconSize(context, 30), ), ), SizedBox(height: screenHeight * 0.02), Text( "Pay Now", style: TextStyle( fontFamily: "Plus Jakarta Sans", fontWeight: FontWeight.w700, fontSize: getResponsiveTextSize(context, 20), color: Colors.black87, ), ), SizedBox(height: screenHeight * 0.015), Text( "Proceed with payment for this bill?", textAlign: TextAlign.center, style: TextStyle( fontFamily: "Plus Jakarta Sans", fontSize: getResponsiveTextSize(context, 14), fontWeight: FontWeight.w400, color: Colors.grey, ), ), SizedBox(height: screenHeight * 0.025), Row( children: [ // Cancel Button Expanded( child: OutlinedButton( onPressed: () => Navigator.pop(context), style: OutlinedButton.styleFrom( backgroundColor: Colors.transparent, foregroundColor: Colors.grey[600], side: BorderSide( color: Colors.grey[300]!, width: 1.5, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(screenWidth * 0.03), ), padding: EdgeInsets.symmetric(vertical: screenHeight * 0.017), ), child: Text( "Cancel", style: TextStyle( fontFamily: "Plus Jakarta Sans", fontWeight: FontWeight.w600, fontSize: getResponsiveTextSize(context, 14), ), ), ), ), SizedBox(width: screenWidth * 0.03), // Proceed Button Expanded( child: ElevatedButton( onPressed: () { Navigator.pop(context); _processPayment(screenWidth, screenHeight); }, style: ElevatedButton.styleFrom( backgroundColor: AppColors.buttonColor, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(screenWidth * 0.03), ), padding: EdgeInsets.symmetric(vertical: screenHeight * 0.017), ), child: Text( "Proceed", style: TextStyle( fontFamily: "Plus Jakarta Sans", fontWeight: FontWeight.w600, fontSize: getResponsiveTextSize(context, 14), color: Colors.white, ), ), ), ), ], ), ], ), ), ); }, ); } /// Process Payment Method void _processPayment(double screenWidth, double screenHeight) { // Show loading showDialog( context: context, barrierDismissible: false, builder: (context) => Center( child: Container( padding: EdgeInsets.all(screenWidth * 0.05), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(screenWidth * 0.04), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator( color: AppColors.buttonColor, strokeWidth: 3, ), SizedBox(height: screenHeight * 0.02), Text( "Processing Payment...", style: TextStyle( fontFamily: "Plus Jakarta Sans", fontWeight: FontWeight.w500, fontSize: getResponsiveTextSize(context, 14), ), ), ], ), ), ), ); // Simulate payment processing Future.delayed(const Duration(seconds: 2), () { Navigator.pop(context); // Close loading // Show success message ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: Colors.green, content: Row( children: [ Icon(Icons.check_circle, color: Colors.white, size: getResponsiveIconSize(context, 20)), SizedBox(width: screenWidth * 0.02), Text( "Payment Successful!", style: TextStyle( fontFamily: "Plus Jakarta Sans", fontWeight: FontWeight.w500, fontSize: getResponsiveTextSize(context, 14), ), ), ], ), duration: const Duration(seconds: 3), behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(screenWidth * 0.02), ), ), ); // Refresh bill details final provider = Provider.of(context, listen: false); provider.fetchBillDetails(widget.sessionId, widget.accId, widget.billId); }); } /// Helper Row Widget Widget _buildRow(String title, String value, {bool isBold = false, bool highlight = false}) { return Padding( padding: EdgeInsets.symmetric(vertical: MediaQuery.of(context).size.height * 0.005), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 7, child: Text( title, maxLines: 2, style: TextStyle( fontFamily: "Poppins", fontSize: getResponsiveTextSize(context, 14), color: AppColors.subtitleText, fontWeight: FontWeight.w400, ), ), ), Text( value, style: TextStyle( fontFamily: "Poppins", fontSize: getResponsiveTextSize(context, 14), fontWeight: isBold ? FontWeight.w700 : FontWeight.w400, color: highlight ? AppColors.normalText : Colors.grey.shade900, ), ), ], ), ); } }