import 'dart:async'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:provider/provider.dart'; import '../../Notifiers/QrProvider.dart'; import '../../Utils/app_colors.dart'; import '../../Utils/commonWidgets.dart'; import '../../Utils/custom_snackbar.dart'; class RazorpayQrScreen extends StatefulWidget { final String sessionId; final String empId; final String amount; final String refType; final String refId; const RazorpayQrScreen({ super.key, required this.sessionId, required this.empId, required this.amount, required this.refType, required this.refId, }); @override State createState() => _RazorpayQrScreenState(); } class _RazorpayQrScreenState extends State { Timer? _statusTimer; @override void initState() { super.initState(); Future.microtask(() async { final qrProvider = context.read(); // Step 1: Create QR await qrProvider.fetchRazorpayQr( sessionId: widget.sessionId, empId: widget.empId, amount: widget.amount, refType: widget.refType, refId: widget.refId, ); // Step 2: Start polling only if QR generated successfully if (qrProvider.qrResponse?.error == "0" && qrProvider.qrResponse?.qrCode != null) { _startStatusPolling(qrProvider); } }); } void _startStatusPolling(QrProvider provider) { _statusTimer = Timer.periodic(const Duration(seconds: 5), (timer) async { if (!mounted) return; final razorpayOrderId = provider.qrResponse?.qrId; if (razorpayOrderId == null) return; final response = await provider.fetchRazorpayUpiQrStatus( context: context, sessionId: widget.sessionId, empId: widget.empId, razorpayOrderId: razorpayOrderId, ); if (response != null && response.error == "0") {// CustomSnackBar.showSuccess( context: context, message:"Payment received successfully ", ); // Fluttertoast.showToast( // msg: // backgroundColor: Colors.green, // textColor: Colors.white, // ); // Stop timer timer.cancel(); // Go back two screens if (mounted) { Navigator.pop(context); // close QR screen Navigator.pop(context); // close previous screen } } }); } @override void dispose() { _statusTimer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: appbarNew(context, "Scan QR to Pay", 0xFFFFFFFF), backgroundColor: Colors.black, body: SafeArea( child: Consumer( builder: (context, provider, _) { if (provider.isLoading) { return _buildLoadingState(); } if (provider.errorMessage != null) { return _buildErrorState(provider.errorMessage!); } if (provider.qrResponse == null || provider.qrResponse?.qrCode == null) { return _buildNoQrState(); } if (provider.secondsLeft == 0) { Future.delayed(Duration.zero, () { if (mounted) Navigator.pop(context); }); return _buildExpiredState(); } return _buildQrScreen(provider); }, ), ), ); } Widget _buildLoadingState() { return Container( color: Colors.black, child: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.white), ), SizedBox(height: 20), Text( "Generating QR Code...", style: TextStyle( color: Colors.white, fontSize: 16, ), ), ], ), ), ); } Widget _buildErrorState(String errorMessage) { return Container( color: Colors.black, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.error_outline, size: 60, color: Colors.redAccent, ), const SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric(horizontal: 40), child: Text( errorMessage, style: const TextStyle( color: Colors.white, fontSize: 16, ), textAlign: TextAlign.center, ), ), const SizedBox(height: 30), _buildBackButton(), ], ), ), ); } Widget _buildNoQrState() { return Container( color: Colors.black, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.qr_code_2, size: 60, color: Colors.grey, ), const SizedBox(height: 20), const Text( "No QR Code Found", style: TextStyle( color: Colors.white, fontSize: 18, ), ), const SizedBox(height: 30), _buildBackButton(), ], ), ), ); } Widget _buildExpiredState() { return Container( color: Colors.black, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.timer_off_rounded, size: 60, color: Colors.redAccent, ), const SizedBox(height: 20), const Text( "QR Expired", style: TextStyle( fontSize: 20, color: Colors.redAccent, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 10), const Text( "Returning to previous screen...", style: TextStyle( color: Colors.white70, fontSize: 14, ), ), ], ), ), ); } Widget _buildQrScreen(QrProvider provider) { final screenHeight = MediaQuery.of(context).size.height; final screenWidth = MediaQuery.of(context).size.width; return Stack( children: [ // Full Screen QR Code Background - Preserving all text Positioned.fill( child: Image.network( provider.qrResponse!.qrCode!, fit: BoxFit.contain, // Use contain to preserve all content loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return Container( color: Colors.black, child: const Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.white), ), ), ); }, errorBuilder: (context, error, stackTrace) { return Container( color: Colors.black, child: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error, size: 60, color: Colors.red), SizedBox(height: 16), Text( "Failed to load QR code", style: TextStyle(color: Colors.white, fontSize: 16), ), ], ), ), ); }, ), ), // Top Bar - Timer and Close Positioned( top: 0, left: 0, right: 0, child: Container( height: 80, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.black.withOpacity(0.8), Colors.transparent, ], ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Timer on left Container( margin: const EdgeInsets.only(left: 20, top: 40), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.orange, width: 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.timer, color: Colors.orange, size: 16, ), const SizedBox(width: 6), Text( "${provider.secondsLeft}s", style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w600, ), ), ], ), ), // Close button on right Container( margin: const EdgeInsets.only(right: 20, top: 40), child: CircleAvatar( radius: 20, backgroundColor: Colors.transparent, child: IconButton( icon: const Icon(Icons.close, size: 18), color: Colors.transparent, onPressed: () => Navigator.pop(context), ), ), ), ], ), ), ), // Bottom Section - Amount and Action Buttons // Positioned at the very bottom to avoid covering QR content Positioned( bottom: 0, left: 0, right: 0, child: Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [ Colors.black.withOpacity(0.9), Colors.black.withOpacity(0.6), Colors.transparent, ], ), ), child: Column( children: [ // Amount Display // Container( // width: double.infinity, // padding: const EdgeInsets.all(16), // decoration: BoxDecoration( // color: AppColors.app_blue.withOpacity(0.9), // borderRadius: BorderRadius.circular(12), // boxShadow: [ // BoxShadow( // color: Colors.black.withOpacity(0.3), // blurRadius: 10, // offset: const Offset(0, 4), // ), // ], // ), // child: Column( // children: [ // const Text( // "Amount to Pay", // style: TextStyle( // color: Colors.white70, // fontSize: 14, // fontWeight: FontWeight.w500, // ), // ), // const SizedBox(height: 8), // Text( // "₹${widget.amount}", // style: const TextStyle( // fontSize: 28, // fontWeight: FontWeight.bold, // color: Colors.white, // ), // ), // ], // ), // ), const SizedBox(height: 20), // Action Buttons Row( children: [ // Cancel Button Expanded( child: ElevatedButton.icon( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.close, size: 18), label: const Text("Cancel"), style: ElevatedButton.styleFrom( backgroundColor: Colors.red.withOpacity(0.9), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), padding: const EdgeInsets.symmetric(vertical: 12), elevation: 3, ), ), ), const SizedBox(width: 12), // Instructions Button Expanded( child: ElevatedButton.icon( onPressed: _showInstructions, icon: const Icon(Icons.help_outline, size: 18), label: const Text("Help"), style: ElevatedButton.styleFrom( backgroundColor: Colors.grey.withOpacity(0.8), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), padding: const EdgeInsets.symmetric(vertical: 12), elevation: 3, ), ), ), ], ), ], ), ), ), // Small status indicator at the top center (minimal space usage) Positioned( top: 40, left: 0, right: 0, child: Center( child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), decoration: BoxDecoration( color: Colors.green.withOpacity(0.9), borderRadius: BorderRadius.circular(12), ), child: const Text( "READY TO SCAN", style: TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w600, letterSpacing: 1.0, ), ), ), ), ), ], ); } void _showInstructions() { showModalBottomSheet( context: context, backgroundColor: Colors.transparent, builder: (context) => Container( decoration: BoxDecoration( color: AppColors.scaffold_bg_color, borderRadius: const BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20), ), ), padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(height: 20), const Text( "How to Pay", style: TextStyle( color: Colors.blue, fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), _buildInstructionStep("1", "Open any UPI app on your phone"), _buildInstructionStep("2", "Tap on 'Scan QR Code'"), _buildInstructionStep("3", "Point your camera at this QR code"), _buildInstructionStep("4", "Enter UPI PIN to complete payment"), const SizedBox(height: 20), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () => Navigator.pop(context), style: ElevatedButton.styleFrom( backgroundColor: AppColors.app_blue, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), padding: const EdgeInsets.symmetric(vertical: 12), ), child: const Text("Got It"), ), ), const SizedBox(height: 10), ], ), ), ); } Widget _buildInstructionStep(String number, String text) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 24, height: 24, decoration: BoxDecoration( color: AppColors.app_blue, shape: BoxShape.circle, ), child: Center( child: Text( number, style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, ), ), ), ), const SizedBox(width: 12), Expanded( child: Text( text, style: const TextStyle( color: Colors.black87, fontSize: 14, ), ), ), ], ), ); } Widget _buildBackButton() { return ElevatedButton.icon( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.arrow_back), label: const Text("Go Back"), style: ElevatedButton.styleFrom( backgroundColor: AppColors.app_blue, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24), ), ); } }