TKK_E32231279/lib/screens/scan_qr_screen.dart

376 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:intl/intl.dart';
class ScanQrScreen extends StatefulWidget {
const ScanQrScreen({super.key});
@override
State<ScanQrScreen> createState() => _ScanQrScreenState();
}
class _ScanQrScreenState extends State<ScanQrScreen> {
static const Color primaryColor = Color(0xFFA5CC36);
final TextEditingController nominalController = TextEditingController();
bool loading = false;
bool isScanned = false;
String? qrData;
String rupiah(int value) {
return NumberFormat.currency(
locale: 'id_ID',
symbol: 'Rp ',
decimalDigits: 0,
).format(value);
}
void addNumber(String number) {
setState(() {
nominalController.text += number;
});
}
void backspace() {
setState(() {
if (nominalController.text.isNotEmpty) {
nominalController.text = nominalController.text.substring(
0,
nominalController.text.length - 1,
);
}
});
}
void clearNominal() {
setState(() {
nominalController.clear();
});
}
void showError(String pesan) {
showDialog(
context: context,
builder: (_) => Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
child: Padding(
padding: const EdgeInsets.all(25),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 90,
height: 90,
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: const Icon(Icons.close, size: 60, color: Colors.white),
),
const SizedBox(height: 20),
const Text(
"Pembayaran Gagal",
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Text(pesan, textAlign: TextAlign.center),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text("OK"),
),
),
],
),
),
),
);
}
void showSuccess(int nominal, int saldoBaru) {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
child: Padding(
padding: const EdgeInsets.all(25),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TweenAnimationBuilder(
duration: const Duration(milliseconds: 700),
tween: Tween(begin: 0.0, end: 1.0),
builder: (context, value, child) {
return Transform.scale(
scale: value,
child: Container(
width: 100,
height: 100,
decoration: const BoxDecoration(
color: Color(0xFFA5CC36),
shape: BoxShape.circle,
),
child: const Icon(
Icons.check,
size: 65,
color: Colors.black,
),
),
);
},
),
const SizedBox(height: 20),
const Text(
"Pembayaran Berhasil",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Text("Nominal pembayaran", textAlign: TextAlign.center),
const SizedBox(height: 5),
Text(
rupiah(nominal),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
const SizedBox(height: 10),
Text(
"Sisa saldo : ${rupiah(saldoBaru)}",
textAlign: TextAlign.center,
),
const SizedBox(height: 25),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
foregroundColor: Colors.black,
),
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
child: const Text("SELESAI"),
),
),
],
),
),
),
);
}
Future<void> prosesPembayaran() async {
final nominal = int.tryParse(nominalController.text) ?? 0;
if (qrData == null) {
showError("QR belum dipindai");
return;
}
if (nominal < 1000) {
showError("Minimal pembayaran Rp1.000");
return;
}
setState(() => loading = true);
try {
final uid = FirebaseAuth.instance.currentUser!.uid;
final firestoreUser = await FirebaseFirestore.instance
.collection("users")
.doc(uid)
.get();
final rfid = firestoreUser.data()?["rfid_uid"]?.toString();
if (rfid == null || rfid.isEmpty) {
throw Exception("RFID user tidak ditemukan");
}
final userRef = FirebaseDatabase.instance.ref("users/$rfid");
final snapshot = await userRef.get();
final data = Map<String, dynamic>.from(snapshot.value as Map);
final saldo = (data["saldo"] ?? 0) as int;
if (saldo < nominal) {
showError("Saldo tidak mencukupi");
return;
}
final saldoBaru = saldo - nominal;
await userRef.update({"saldo": saldoBaru});
await userRef.child("history").push().set({
"type": "Pembayaran QRIS",
"nilai": nominal,
"waktu": DateTime.now().toIso8601String(),
"saldo_sebelum": saldo,
"saldo_sesudah": saldoBaru,
"qr_data": qrData,
});
if (mounted) {
showSuccess(nominal, saldoBaru);
}
} catch (e) {
showError(e.toString());
} finally {
if (mounted) {
setState(() => loading = false);
}
}
}
Widget buildKeypad() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (var row in [
["1", "2", "3"],
["4", "5", "6"],
["7", "8", "9"],
["C", "0", ""],
])
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: row.map((e) {
return Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: SizedBox(
height: 60,
child: ElevatedButton(
onPressed: () {
if (e == "C") {
clearNominal();
} else if (e == "") {
backspace();
} else {
addNumber(e);
}
},
child: Text(e),
),
),
),
);
}).toList(),
),
),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF0B1600),
appBar: AppBar(
title: const Text("Scan QRIS"),
backgroundColor: primaryColor,
),
body: qrData == null
? Stack(
children: [
MobileScanner(
onDetect: (capture) {
if (isScanned) return;
final code = capture.barcodes.first.rawValue;
if (code != null) {
setState(() {
isScanned = true;
qrData = code;
});
}
},
),
Center(
child: Container(
width: 260,
height: 260,
decoration: BoxDecoration(
border: Border.all(color: primaryColor, width: 4),
borderRadius: BorderRadius.circular(20),
),
),
),
],
)
: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
const Text(
"QRIS Berhasil Dipindai",
style: TextStyle(
color: Colors.white,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
Container(
width: double.infinity,
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: const Color(0xFF1A1A1A),
borderRadius: BorderRadius.circular(12),
),
child: Text(
nominalController.text.isEmpty
? "Rp 0"
: "Rp ${nominalController.text}",
style: const TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 20),
Expanded(child: buildKeypad()),
SizedBox(
width: double.infinity,
height: 55,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
foregroundColor: Colors.black,
),
onPressed: loading ? null : prosesPembayaran,
child: loading
? const CircularProgressIndicator()
: const Text("Bayar"),
),
),
],
),
),
);
}
}