import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cashumit/services/firefly_api_service.dart'; import 'package:bluetooth_print/bluetooth_print.dart'; import 'package:bluetooth_print/bluetooth_print_model.dart'; import 'package:cashumit/widgets/custom_text_config_dialog.dart'; // Tambahkan import ini class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); @override State createState() => _SettingsScreenState(); } class _SettingsScreenState extends State { final _formKey = GlobalKey(); final _urlController = TextEditingController(); final _tokenController = TextEditingController(); bool _isTestingConnection = false; bool _isTestingAuth = false; // Bluetooth printer variables BluetoothPrint bluetoothPrint = BluetoothPrint.instance; bool _isScanning = false; List _devices = []; BluetoothDevice? _selectedDevice; bool _connected = false; @override void initState() { super.initState(); _loadSettings(); _initBluetooth(); } @override void dispose() { _urlController.dispose(); _tokenController.dispose(); super.dispose(); } Future _loadSettings() async { final prefs = await SharedPreferences.getInstance(); // Tambahkan pengecekan mounted sebelum setState if (mounted) { setState(() { _urlController.text = prefs.getString('firefly_url') ?? ''; _tokenController.text = prefs.getString('firefly_token') ?? ''; }); } } Future _initBluetooth() async { // Periksa status koneksi final isConnected = await bluetoothPrint.isConnected ?? false; if (mounted) { setState(() { _connected = isConnected; }); } // Listen to bluetooth state changes bluetoothPrint.state.listen((state) { if (mounted) { switch (state) { case BluetoothPrint.CONNECTED: setState(() { _connected = true; }); break; case BluetoothPrint.DISCONNECTED: setState(() { _connected = false; _selectedDevice = null; }); break; default: break; } } }); // Load saved device await _loadSavedBluetoothDevice(); } /// Memuat device bluetooth yang tersimpan Future _loadSavedBluetoothDevice() async { final prefs = await SharedPreferences.getInstance(); final deviceAddress = prefs.getString('bluetooth_device_address'); final deviceName = prefs.getString('bluetooth_device_name'); if (deviceAddress != null && deviceName != null) { final device = BluetoothDevice(); device.name = deviceName; device.address = deviceAddress; if (mounted) { setState(() { _selectedDevice = device; }); } } } /// Menyimpan device bluetooth yang terhubung Future _saveBluetoothDevice(BluetoothDevice device) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('bluetooth_device_address', device.address ?? ''); await prefs.setString('bluetooth_device_name', device.name ?? ''); } Future _saveSettings() async { if (_formKey.currentState!.validate()) { final prefs = await SharedPreferences.getInstance(); await prefs.setString('firefly_url', _urlController.text.trim()); await prefs.setString('firefly_token', _tokenController.text.trim()); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Pengaturan berhasil disimpan')), ); } } } Future _testConnection() async { // Tambahkan pengecekan mounted if (!mounted) return; setState(() { _isTestingConnection = true; }); // Simpan pengaturan terlebih dahulu await _saveSettings(); // Uji koneksi final success = await FireflyApiService.testConnection(baseUrl: _urlController.text.trim()); // Tambahkan pengecekan mounted sebelum setState if (mounted) { setState(() { _isTestingConnection = false; }); if (success) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Koneksi berhasil!')), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Koneksi gagal. Periksa URL dan pastikan Firefly III berjalan.')), ); } } } Future _testAuthentication() async { // Tambahkan pengecekan mounted if (!mounted) return; setState(() { _isTestingAuth = true; }); // Simpan pengaturan terlebih dahulu await _saveSettings(); // Uji autentikasi final success = await FireflyApiService.testAuthentication(baseUrl: _urlController.text.trim(), accessToken: _tokenController.text.trim()); // Tambahkan pengecekan mounted sebelum setState if (mounted) { setState(() { _isTestingAuth = false; }); if (success) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Autentikasi berhasil!')), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Autentikasi gagal. Periksa token yang digunakan.')), ); } } } Future _scanDevices() async { setState(() { _isScanning = true; _devices = []; }); try { // Mulai scan perangkat Bluetooth await bluetoothPrint.startScan(timeout: const Duration(seconds: 4)); // Listen to scan results bluetoothPrint.scanResults.listen((devices) { if (mounted) { setState(() { _devices = devices; }); } }); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Gagal memindai perangkat: $e')), ); } } finally { if (mounted) { setState(() { _isScanning = false; }); } } } Future _connectToDevice(BluetoothDevice device) async { try { await bluetoothPrint.connect(device); setState(() { _selectedDevice = device; }); // Simpan device yang dipilih await _saveBluetoothDevice(device); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Berhasil terhubung ke printer')), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Gagal terhubung ke printer: $e')), ); } } } /// Memutuskan koneksi dari printer bluetooth Future _disconnect() async { try { await bluetoothPrint.disconnect(); setState(() { _connected = false; _selectedDevice = null; }); // Hapus device yang tersimpan final prefs = await SharedPreferences.getInstance(); await prefs.remove('bluetooth_device_address'); await prefs.remove('bluetooth_device_name'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Berhasil memutus koneksi dari printer')), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Gagal memutus koneksi dari printer: $e')), ); } } } /// Membuka dialog konfigurasi teks kustom Future _openCustomTextConfig() async { final result = await showDialog( context: context, builder: (context) => const CustomTextConfigDialog(), // Tambahkan import di bagian atas ); // Jika teks kustom berhasil disimpan, tampilkan snackbar if (result == true && mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Teks kustom berhasil disimpan')), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Pengaturan'), centerTitle: true, ), body: Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // === Firefly III Settings === const Text( 'Koneksi ke Firefly III', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), const Text( 'Masukkan URL lengkap ke instance Firefly III Anda (contoh: https://firefly.example.com)', ), const SizedBox(height: 8), TextFormField( controller: _urlController, decoration: const InputDecoration( labelText: 'Firefly III URL', border: OutlineInputBorder(), hintText: 'https://firefly.example.com', ), validator: (value) { if (value == null || value.isEmpty) { return 'Mohon masukkan URL Firefly III'; } return null; }, ), const SizedBox(height: 16), const Text( 'Masukkan Personal Access Token dari Firefly III', ), const SizedBox(height: 8), TextFormField( controller: _tokenController, decoration: const InputDecoration( labelText: 'Access Token', border: OutlineInputBorder(), ), obscureText: true, validator: (value) { if (value == null || value.isEmpty) { return 'Mohon masukkan access token'; } return null; }, ), const SizedBox(height: 16), Row( children: [ Expanded( child: ElevatedButton( onPressed: _isTestingConnection ? null : _testConnection, child: _isTestingConnection ? const Text('Menguji Koneksi...') : const Text('Uji Koneksi'), ), ), const SizedBox(width: 16), Expanded( child: ElevatedButton( onPressed: _isTestingAuth ? null : _testAuthentication, child: _isTestingAuth ? const Text('Menguji Auth...') : const Text('Uji Auth'), ), ), ], ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _saveSettings, child: const Text('Simpan Pengaturan Firefly III'), ), ), const SizedBox(height: 32), // === Printer Settings === const Text( 'Pengaturan Printer Bluetooth', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), const Text( 'Hubungkan printer thermal Anda melalui Bluetooth untuk mencetak struk.', ), const SizedBox(height: 16), // Status koneksi Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('Status Printer:'), Text( _connected ? 'Terhubung' : 'Terputus', style: TextStyle( color: _connected ? Colors.green : Colors.red, fontWeight: FontWeight.bold, ), ), ], ), ), const SizedBox(height: 16), // Tombol scan ElevatedButton.icon( onPressed: _isScanning ? null : _scanDevices, icon: _isScanning ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.bluetooth_searching), label: Text(_isScanning ? 'Memindai...' : 'Cari Printer'), ), const SizedBox(height: 16), // Daftar perangkat SizedBox( height: 200, // Tentukan tinggi tetap untuk daftar child: _devices.isEmpty ? const Center( child: Text('Tidak ada perangkat ditemukan'), ) : ListView.builder( itemCount: _devices.length, itemBuilder: (context, index) { final device = _devices[index]; final isSelected = _selectedDevice?.address == device.address; return Card( child: ListTile( title: Text(device.name ?? 'Unknown Device'), subtitle: Text(device.address ?? ''), trailing: isSelected ? const Icon(Icons.check, color: Colors.green) : null, onTap: () => _connectToDevice(device), ), ); }, ), ), // Tombol disconnect if (_connected) Padding( padding: const EdgeInsets.only(top: 16), child: ElevatedButton.icon( onPressed: _disconnect, icon: const Icon(Icons.bluetooth_disabled), label: const Text('Putus Koneksi'), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, ), ), ), ), ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () { // Fungsi tambahan jika diperlukan }, child: const Text('Simpan Pengaturan Printer'), ), ), const SizedBox(height: 32), // === Custom Text Settings === const Text( 'Teks Kustom Struk', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), const Text( 'Sesuaikan teks disclaimer, ucapan terima kasih, dan pantun di struk Anda.', ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _openCustomTextConfig, child: const Text('Edit Teks Kustom'), ), ), ], ), ), ), ); } }