374 lines
11 KiB
Dart
374 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:firebase_database/firebase_database.dart';
|
|
|
|
class SendScreen extends StatefulWidget {
|
|
final String myRfid;
|
|
|
|
const SendScreen({super.key, required this.myRfid});
|
|
|
|
@override
|
|
State<SendScreen> createState() => _SendScreenState();
|
|
}
|
|
|
|
class _SendScreenState extends State<SendScreen> {
|
|
final rfidController = TextEditingController();
|
|
final nominalController = TextEditingController();
|
|
|
|
bool loading = false;
|
|
|
|
static const Color primaryColor = Color(0xFFA5CC36);
|
|
|
|
// =========================
|
|
// KEYBOARD CUSTOM
|
|
// =========================
|
|
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();
|
|
});
|
|
}
|
|
|
|
// =========================
|
|
// ERROR DIALOG
|
|
// =========================
|
|
void _showErrorDialog(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(
|
|
"Transfer 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"),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// =========================
|
|
// SUCCESS DIALOG
|
|
// =========================
|
|
void _showSuccessDialog(int nominal, String tujuan) {
|
|
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(
|
|
"Transfer Berhasil",
|
|
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 10),
|
|
Text(
|
|
"Rp $nominal berhasil dikirim ke",
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 5),
|
|
Text(
|
|
tujuan,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 18,
|
|
),
|
|
),
|
|
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"),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// =========================
|
|
// TRANSFER LOGIC
|
|
// =========================
|
|
Future<void> kirimSaldo() async {
|
|
final tujuan = rfidController.text.trim();
|
|
final nominal = int.tryParse(nominalController.text) ?? 0;
|
|
|
|
if (tujuan.isEmpty) {
|
|
_showErrorDialog("RFID tujuan wajib diisi");
|
|
return;
|
|
}
|
|
|
|
if (tujuan == widget.myRfid) {
|
|
_showErrorDialog("Tidak dapat transfer ke akun sendiri");
|
|
return;
|
|
}
|
|
|
|
if (nominal < 1000) {
|
|
_showErrorDialog("Minimal transfer Rp1.000");
|
|
return;
|
|
}
|
|
|
|
setState(() => loading = true);
|
|
|
|
try {
|
|
final db = FirebaseDatabase.instance.ref();
|
|
|
|
final pengirimSnap = await db.child("users/${widget.myRfid}").get();
|
|
|
|
final penerimaSnap = await db.child("users/$tujuan").get();
|
|
|
|
if (!penerimaSnap.exists) {
|
|
_showErrorDialog("RFID tujuan tidak ditemukan");
|
|
return;
|
|
}
|
|
|
|
final saldoPengirim =
|
|
(pengirimSnap.child("saldo").value as num?)?.toInt() ?? 0;
|
|
|
|
final saldoPenerima =
|
|
(penerimaSnap.child("saldo").value as num?)?.toInt() ?? 0;
|
|
|
|
if (saldoPengirim < nominal) {
|
|
_showErrorDialog("Saldo tidak mencukupi");
|
|
return;
|
|
}
|
|
|
|
await db
|
|
.child("users/${widget.myRfid}/saldo")
|
|
.set(saldoPengirim - nominal);
|
|
|
|
await db.child("users/$tujuan/saldo").set(saldoPenerima + nominal);
|
|
|
|
final now = DateTime.now().toIso8601String();
|
|
|
|
await db.child("users/${widget.myRfid}/history").push().set({
|
|
"type": "transfer_keluar",
|
|
"nilai": nominal,
|
|
"tujuan": tujuan,
|
|
"waktu": now,
|
|
});
|
|
|
|
await db.child("users/$tujuan/history").push().set({
|
|
"type": "transfer_masuk",
|
|
"nilai": nominal,
|
|
"dari": widget.myRfid,
|
|
"waktu": now,
|
|
});
|
|
|
|
if (!mounted) return;
|
|
|
|
_showSuccessDialog(nominal, tujuan);
|
|
} catch (e) {
|
|
_showErrorDialog(e.toString());
|
|
} finally {
|
|
setState(() => loading = false);
|
|
}
|
|
}
|
|
|
|
// =========================
|
|
// KEYPAD UI
|
|
// =========================
|
|
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) {
|
|
final bool isAction = e == "C" || e == "⌫";
|
|
|
|
return Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 5),
|
|
child: SizedBox(
|
|
height: 60, // 🔥 penting: biar tidak terlalu tinggi
|
|
child: ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: isAction
|
|
? (e == "C"
|
|
? Colors.redAccent
|
|
: Colors.orangeAccent)
|
|
: const Color(0xFF2C2C2C),
|
|
foregroundColor: Colors.white,
|
|
elevation: 2,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
side: const BorderSide(color: Color(0xFF3A3A3A)),
|
|
),
|
|
),
|
|
onPressed: () {
|
|
if (e == "C") {
|
|
clearNominal();
|
|
} else if (e == "⌫") {
|
|
backspace();
|
|
} else {
|
|
addNumber(e);
|
|
}
|
|
},
|
|
child: Text(
|
|
e,
|
|
style: const TextStyle(
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFF0B1600),
|
|
appBar: AppBar(
|
|
backgroundColor: primaryColor,
|
|
title: const Text("Kirim Saldo"),
|
|
),
|
|
body: Padding(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
children: [
|
|
TextField(
|
|
controller: rfidController,
|
|
style: const TextStyle(color: Colors.white),
|
|
decoration: const InputDecoration(
|
|
labelText: "RFID UID Tujuan",
|
|
labelStyle: TextStyle(color: Colors.white70),
|
|
),
|
|
),
|
|
|
|
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),
|
|
onPressed: loading ? null : kirimSaldo,
|
|
child: loading
|
|
? const CircularProgressIndicator(color: Colors.black)
|
|
: const Text("Kirim"),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|