import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; class UserPage extends StatefulWidget { @override _UserPageState createState() => _UserPageState(); } //Inisialisasi dan State Management class _UserPageState extends State { BluetoothState _bluetoothState = BluetoothState.UNKNOWN; final GlobalKey _scaffoldKey = GlobalKey(); final FlutterBluetoothSerial _bluetooth = FlutterBluetoothSerial.instance; BluetoothConnection? connection; late int _deviceState; bool isDisconnecting = false; Map colors = { 'onBorderColor': Colors.green, 'offBorderColor': Colors.red, 'neutralBorderColor': Colors.transparent, 'onTextColor': Colors.green, 'offTextColor': Colors.red, 'neutralTextColor': Colors.blue, }; bool? get isConnected => connection != null && connection!.isConnected; List _devicesList = []; BluetoothDevice? _device; bool _connected = false; bool _isButtonUnavailable = false; bool _isLoading = false; //Dipanggil ketika widget diinisialisasi. Digunakan untuk mengatur state awal dan mengaktifkan Bluetooth. @override void initState() { super.initState(); FlutterBluetoothSerial.instance.state.then((state) { setState(() { _bluetoothState = state; }); }); _deviceState = 0; enableBluetooth(); FlutterBluetoothSerial.instance .onStateChanged() .listen((BluetoothState state) { setState(() { _bluetoothState = state; if (_bluetoothState == BluetoothState.STATE_OFF) { _isButtonUnavailable = true; } getPairedDevices(); }); }); } //Dipanggil ketika widget dihancurkan. Digunakan untuk membersihkan resource yang digunakan. @override void dispose() { if (isConnected!) { isDisconnecting = true; connection?.dispose(); connection = null; } super.dispose(); } //Mengaktifkan Bluetooth jika tidak aktif. Future enableBluetooth() async { _bluetoothState = await FlutterBluetoothSerial.instance.state; if (_bluetoothState == BluetoothState.STATE_OFF) { await FlutterBluetoothSerial.instance.requestEnable(); await getPairedDevices(); } else { await getPairedDevices(); } } //Mendapatkan daftar perangkat yang sudah terhubung Future getPairedDevices() async { List devices = []; try { devices = await _bluetooth.getBondedDevices(); } on PlatformException { debugPrint("Error"); } if (!mounted) { return; } setState(() { _devicesList = devices; }); } //ui @override Widget build(BuildContext context) { return Scaffold( key: _scaffoldKey, appBar: AppBar( title: const Text("Smart Door"), backgroundColor: Color.fromARGB(255, 243, 146, 34), actions: [ ElevatedButton.icon( icon: const Icon( Icons.refresh, color: Color.fromARGB(255, 0, 0, 0), ), label: const Text( "", style: TextStyle( color: Colors.white, ), ), onPressed: () async { await getPairedDevices().then((_) { show('Daftar perangkat diperbarui'); }); }, ), ], ), body: Column( mainAxisSize: MainAxisSize.max, children: [ Visibility( visible: _isLoading, child: LinearProgressIndicator( backgroundColor: Colors.yellow, valueColor: AlwaysStoppedAnimation(Colors.red), ), ), Visibility( visible: _isButtonUnavailable && _bluetoothState == BluetoothState.STATE_ON, child: const LinearProgressIndicator( backgroundColor: Colors.yellow, valueColor: AlwaysStoppedAnimation(Colors.red), ), ), Padding( padding: const EdgeInsets.all(10), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ const Expanded( child: Text( 'Nyalakan Bluetooth', style: TextStyle( color: Colors.black, fontSize: 16, ), ), ), Switch( value: _bluetoothState.isEnabled, onChanged: (bool value) { future() async { if (value) { await FlutterBluetoothSerial.instance.requestEnable(); } else { await FlutterBluetoothSerial.instance.requestDisable(); } await getPairedDevices(); _isButtonUnavailable = false; if (_connected) { _disconnect(); } } future().then((_) { setState(() {}); }); }, ) ], ), ), Stack( children: [ Column( children: [ const Padding( padding: EdgeInsets.only(top: 10), child: Text( "PERANGKAT TERPASANG", style: TextStyle( fontSize: 22, color: Color.fromARGB(255, 0, 0, 0), fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), ), Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'Perangkat:', style: TextStyle( fontWeight: FontWeight.bold, ), ), SizedBox(width: 10), DropdownButton( items: _getDeviceItems(), onChanged: (value) { setState(() { _device = value; _isButtonUnavailable = false; }); }, value: _devicesList.isNotEmpty ? _device : null, ), ], ), ), ), ], ), Container( color: Colors.blue, ), ], ), Expanded( child: Center( child: ElevatedButton( onPressed: _device != null ? _handleUnlock : null, child: const Icon(Icons.lock_open, size: 100), style: ElevatedButton.styleFrom( shape: CircleBorder(), padding: EdgeInsets.all(40), backgroundColor: Color.fromARGB(255, 251, 252, 255), ), ), ), ), Expanded( child: Padding( padding: const EdgeInsets.all(20), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( "CATATAN: Jika Anda tidak dapat menemukan perangkat dalam daftar, silakan pasangkan perangkat melalui pengaturan Bluetooth", style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.red, ), ), const SizedBox(height: 15), ElevatedButton( child: const Text("Pengaturan Bluetooth"), onPressed: () { FlutterBluetoothSerial.instance.openSettings(); }, ), ], ), ), ), ) ], ), ); } //Membuat daftar item dropdown dari perangkat Bluetooth yang tersedia. List> _getDeviceItems() { List> items = []; if (_devicesList.isEmpty) { items.add(const DropdownMenuItem( child: Text('TIDAK ADA'), )); } else { for (var device in _devicesList) { items.add(DropdownMenuItem( value: device, child: Text(device.name!), )); } } return items; } //Menghubungkan ke perangkat Bluetooth yang dipilih. Future _connect() async { if (_device == null) { show('Tidak ada perangkat yang dipilih'); setState(() { _isButtonUnavailable = false; _isLoading = false; }); } else { if (!isConnected!) { setState(() { _isLoading = true; }); try { connection = await BluetoothConnection.toAddress(_device!.address) .timeout(Duration(seconds: 5)); debugPrint('Terhubung ke perangkat'); setState(() { _connected = true; _isButtonUnavailable = false; _isLoading = false; }); connection?.input?.listen((Uint8List data) { final message = utf8.decode(data); debugPrint('Data masuk: $message'); setState(() { _deviceState = data.isNotEmpty ? data.last : 0; }); }).onDone(() { debugPrint('Terputus oleh permintaan jarak jauh'); if (isDisconnecting) { debugPrint('Memutuskan secara lokal!'); } else { show('Terputus dari jarak jauh'); } if (mounted) { setState(() {}); } }); } catch (error) { debugPrint('Tidak dapat terhubung, terjadi pengecualian'); debugPrint(error.toString()); setState(() { _isButtonUnavailable = false; _isLoading = false; }); show('Tidak dapat terhubung dalam waktu 5 detik'); } } } } //Memutuskan koneksi Bluetooth. void _disconnect() async { setState(() { _isButtonUnavailable = true; _deviceState = 0; }); await connection?.close(); show('Perangkat terputus'); if (!connection!.isConnected) { setState(() { _connected = false; _isButtonUnavailable = false; }); } } //Mengirim pesan untuk membuka pintu ke perangkat Bluetooth. void _sendUnlockMessage() async { final jsonData = jsonEncode({ 'action': 'open_door', 'name': 'User', 'address': _device?.address ?? 'unknown_address' }); connection?.output.add(Uint8List.fromList(utf8.encode(jsonData))); await connection?.output.allSent; show('Sinyal buka kunci dikirim'); setState(() { _deviceState = 1; // State unlocked }); Future.delayed(Duration(seconds: 5), _disconnect); } //Mengelola proses pembukaan pintu void _handleUnlock() async { setState(() { _isLoading = true; }); if (!_connected) { await _connect(); } if (_connected) { _sendUnlockMessage(); } } //Menampilkan pesan menggunakan SnackBar. Future show(String message, {Duration duration = const Duration(seconds: 3)}) async { await Future.delayed(const Duration(milliseconds: 100)); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), duration: duration, ), ); } }