TKK_E32210053/lib/user_page.dart

413 lines
12 KiB
Dart

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<UserPage> {
BluetoothState _bluetoothState = BluetoothState.UNKNOWN;
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final FlutterBluetoothSerial _bluetooth = FlutterBluetoothSerial.instance;
BluetoothConnection? connection;
late int _deviceState;
bool isDisconnecting = false;
Map<String, Color> 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<BluetoothDevice> _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<void> 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<void> getPairedDevices() async {
List<BluetoothDevice> 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: <Widget>[
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: <Widget>[
Visibility(
visible: _isLoading,
child: LinearProgressIndicator(
backgroundColor: Colors.yellow,
valueColor: AlwaysStoppedAnimation<Color>(Colors.red),
),
),
Visibility(
visible: _isButtonUnavailable &&
_bluetoothState == BluetoothState.STATE_ON,
child: const LinearProgressIndicator(
backgroundColor: Colors.yellow,
valueColor: AlwaysStoppedAnimation<Color>(Colors.red),
),
),
Padding(
padding: const EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
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: <Widget>[
Column(
children: <Widget>[
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: <Widget>[
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: <Widget>[
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<DropdownMenuItem<BluetoothDevice>> _getDeviceItems() {
List<DropdownMenuItem<BluetoothDevice>> 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<void> _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,
),
);
}
}