import 'dart:async'; import 'dart:convert'; import 'package:app_ta/theme.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; class DetailTest extends StatefulWidget { final String ip; DetailTest(this.ip); @override State createState() => _DetailTestState(); } class _DetailTestState extends State { TextEditingController durasi = TextEditingController(); Timer? timer; // Gunakan nullable Timer int sec = 0; bool isLoading = true; // Tambahkan variabel untuk menunjukkan apakah sedang loading @override void initState() { super.initState(); startTimer(); // Memulai timer saat initState dipanggil } void startTimer() { timer = Timer.periodic(Duration(seconds: 1), (Timer t) { // Kirim permintaan ke ESP32 untuk mendapatkan waktu http.get(Uri.parse('http://${widget.ip}/get-time')).then((response) { // Periksa apakah respons berhasil dan memiliki data if (response.statusCode == 200) { // Ubah respons JSON menjadi Map Map data = jsonDecode(response.body); // Perbarui waktu sesuai respons dari ESP32 setState(() { // Ambil nilai jam, menit, dan detik dari respons int hours = data['hours']; int minutes = data['minutes']; int seconds = data['seconds']; // Ubah waktu menjadi detik dan perbarui state sec = hours * 3600 + minutes * 60 + seconds; isLoading = false; // Setelah waktu berhasil diambil, loading selesai }); // Jika waktu habis, atur nilai sec menjadi 0 if (sec <= 0) { t.cancel(); } } else { // Jika respons gagal, tampilkan pesan kesalahan di konsol print('Failed to fetch time: ${response.statusCode}'); } }).catchError((error) { // Tangani kesalahan jaringan jika terjadi print('Error fetching time: $error'); }); }); } @override void dispose() { // Batalkan timer saat widget di dispose timer?.cancel(); super.dispose(); } void onIncrementTime(int timeInMilliseconds) async { // Kirim durasi waktu ke ESP32 dalam milidetik melalui HTTP POST await http.post( Uri.parse('http://${widget.ip}/set-timer'), body: {'timer': timeInMilliseconds.toString()}, ).then((response) { if (response.statusCode == 200) { // Jika waktu berhasil diatur pada ESP32 showBottomNotification(context, 'timer telah diatur.'); } }).catchError((error) { // Tangani kesalahan jika gagal mengatur waktu pada ESP32 print('Error setting timer: $error'); }); if (sec <= 0 && !timer!.isActive) { // Jika waktu habis dan timer berhenti, mulai timer kembali startTimer(); } else { // Jika waktu belum habis, tambahkan waktu baru ke nilai sec yang ada setState(() { sec += timeInMilliseconds ~/ 1000; // Konversi waktu ke detik }); } } void resetTimer() async { // Kirim permintaan ke ESP32 untuk mereset timer await http.get(Uri.parse('http://${widget.ip}/reset-timer')).then((response) { if (response.statusCode == 200) { // Jika berhasil mereset timer, atur nilai sec menjadi 0 setState(() { sec = 0; }); showBottomNotification(context, 'timer telah direset.'); } else { // Jika gagal mereset timer, tampilkan pesan kesalahan di konsol print('Failed to reset timer: ${response.statusCode}'); } }).catchError((error) { // Tangani kesalahan jaringan jika terjadi print('Error resetting timer: $error'); }); } void showBottomNotification(BuildContext context, String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message, style: GoogleFonts.poppins(),), duration: Duration(seconds: 2), behavior: SnackBarBehavior.floating, backgroundColor: primaryColor, ), ); } Widget header() { return Center( child: Column( children: [ SizedBox( height: 16, ), Container( height: 90, width: 90, decoration: BoxDecoration( image: DecorationImage( image: AssetImage('assets/images/ps.png'), )), ), Text( '${widget.ip}', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), ) ], ), ); } Widget countDown() { int hours = sec ~/ 3600; int minutes = (sec % 3600) ~/ 60; int seconds = sec % 60; String hoursStr = hours.toString().padLeft(2, '0'); String minutesStr = minutes.toString().padLeft(2, '0'); String secondsStr = seconds.toString().padLeft(2, '0'); return Center( child: Container( margin: EdgeInsets.only(top: 36), height: 84, width: 400, decoration: BoxDecoration( borderRadius: BorderRadius.circular(14), border: Border.all(width: 2, color: primaryColor)), child: Center( child: Text( '$hoursStr:$minutesStr:$secondsStr', style: GoogleFonts.spaceMono(fontSize: 52, fontWeight: FontWeight.bold, color: Colors.red), ), ), ), ); } Widget billing() { return Container( margin: EdgeInsets.only(top: 36), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Tambah Durasi Sewa', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), SizedBox(height: 8), Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( margin: EdgeInsets.only(top: 8), height: 46, width: 164, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), border: Border.all(width: 2, color: primaryColor)), child: Center( child: Container( margin: EdgeInsets.only(left: 10), child: TextFormField( controller: durasi, keyboardType: TextInputType.number, style: primaryTextStyle, cursorColor: primaryColor, decoration: InputDecoration.collapsed( hintText: 'masukkan durasi', hintStyle: GoogleFonts.poppins(fontSize: 12)), ), ), ), ), SizedBox( width: 14, ), Container( child: Center( child: Text( 'menit', style: primaryTextStyle.copyWith(fontSize: 16), ), ), ) ], ), Container( margin: EdgeInsets.only(top: 20), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ GestureDetector( onTap: () { setState(() { durasi.text = "30"; }); }, child: Container( height: 38, width: 102, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: primaryColor), child: Center( child: Text( '30 menit', style: secondTextStyle, )), ), ), GestureDetector( onTap: () { setState(() { durasi.text = "60"; }); }, child: Container( height: 38, width: 102, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: primaryColor), child: Center( child: Text( '60 menit', style: secondTextStyle, )), ), ), GestureDetector( onTap: () { setState(() { durasi.text = "90"; }); }, child: Container( height: 38, width: 102, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: primaryColor), child: Center( child: Text( '90 menit', style: secondTextStyle, )), ), ), ], ), Container( margin: EdgeInsets.only(top: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ GestureDetector( onTap: () { setState(() { durasi.text = "120"; }); }, child: Container( height: 38, width: 102, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: primaryColor), child: Center( child: Text( '120 menit', style: secondTextStyle, )), ), ), GestureDetector( onTap: () { setState(() { durasi.text = "180"; }); }, child: Container( height: 38, width: 102, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: primaryColor), child: Center( child: Text( '180 menit', style: secondTextStyle, )), ), ), GestureDetector( onTap: () { setState(() { durasi.text = "240"; }); }, child: Container( height: 38, width: 102, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: primaryColor), child: Center( child: Text( '240 menit', style: secondTextStyle, )), ), ), ], ), ) ], ), ) ], ), ); } Widget button() { return Center( child: Container( margin: EdgeInsets.only(top: 14), height: 44, width: 246, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: primaryColor), child: TextButton( onPressed: () { // Konversi input dari menit menjadi milidetik int durationInMilliseconds = int.parse(durasi.text) * 60 * 1000; onIncrementTime(durationInMilliseconds); }, child: Text('Set', style: secondTextStyle), ), ), ); } Widget resetButton() { return Center( child: Column( children: [ Container( margin: EdgeInsets.only(top: 14), height: 44, width: 246, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.red), child: TextButton( onPressed: () { resetTimer(); // Panggil fungsi resetTimer saat tombol ditekan }, child: Text('Reset', style: secondTextStyle.copyWith(color: Colors.white)), ), ), SizedBox(height: 14,), Text('setiap waktu habis jangan lupa klik reset :)', style: primaryTextStyle,) ], ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( onPressed: () { Navigator.pop(context); }, icon: Icon(Icons.arrow_back_ios_new_rounded, color: primaryColor,), ), ), body: isLoading // Tampilkan loading widget jika sedang loading ? Center( child: CircularProgressIndicator( color: primaryColor, ), ) : Container( margin: EdgeInsets.symmetric(horizontal: 30), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ header(), countDown(), billing(), button(), resetButton() ], ), ), ), ); } }