semesterprojekt-bluetooth-p.../lib/screens/bluetooth_screen.dart

283 lines
9.7 KiB
Dart

import 'dart:async';
import 'package:app_settings/app_settings.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:permission_handler/permission_handler.dart';
import '../objects/bluetooth_device_entry.dart';
import '../objects/bluetooth_object.dart';
import '../screens/bluetooth_device_settings.dart';
import '../widgets/custom_list_tile.dart';
import '../widgets/single_section.dart';
class BluetoothScreen extends StatefulWidget {
final bool start = true;
const BluetoothScreen({super.key});
@override
State<BluetoothScreen> createState() => _BluetoothScreen();
}
// debugPrint("r: ${r.device.name} ${r.device.address} ${r.rssi}");
class _BluetoothScreen extends State<BluetoothScreen> {
final List<BluetoothObject> _discoveryResults = [];
late BluetoothObject? _activeObject;
late Stream<BluetoothDiscoveryResult>? _stream;
late StreamSubscription<BluetoothDiscoveryResult>? _streamSubscription;
bool _isDiscovering = false;
bool _enabledPermissions = false;
bool _enabledBluetooth = false;
ButtonStyle buttonStyle = ElevatedButton.styleFrom(
foregroundColor: Colors.black,
backgroundColor: const Color(0xFFFDE100), // Text Color (Foreground color)
);
bool get _hasActiveObject {
return _activeObject == null ? false : true;
}
@override
void initState() {
_enablePermissions();
_enableBluetooth();
super.initState();
_activeObject = null;
_streamSubscription = null;
if (widget.start) {
_initAsync();
}
}
@override
void dispose() {
// Avoid memory leak (`setState` after dispose) and cancel discovery
_streamSubscription?.cancel();
_activeObject?.disconnectDevice();
super.dispose();
debugPrint("called dispose");
}
Future<void> _initAsync() async {
await _enablePermissions();
await _enableBluetooth();
await _startDiscovery();
}
Future<void> _enablePermissions() async {
PermissionStatus bluetoothScan = await Permission.bluetoothScan.request();
PermissionStatus bluetoothConnect = await Permission.bluetoothConnect.request();
bool granted = bluetoothScan.isGranted && bluetoothConnect.isGranted;
setState(() {
_enabledPermissions = granted;
});
}
Future<void> _enableBluetooth() async {
if (_enabledBluetooth) {
return;
}
BluetoothState state = await FlutterBluetoothSerial.instance.state;
if (state == BluetoothState.STATE_ON) {
setState(() {
_enabledBluetooth = true;
});
return;
}
bool? enabled = await FlutterBluetoothSerial.instance.requestEnable();
enabled ??= false;
setState(() {
_enabledBluetooth = enabled!;
});
}
Future<void> _startDiscovery() async {
debugPrint("enabled: $_enabledPermissions");
if (!_enabledPermissions && !_enabledBluetooth) {
return;
}
setState(() => _isDiscovering = true);
_stream = FlutterBluetoothSerial.instance.startDiscovery();
_streamSubscription = _stream!.listen(_discoveryOnListen);
_streamSubscription!.onDone(_discoveryOnDone);
}
Future<void> _discoveryOnDone() async {
setState(() => _isDiscovering = false);
}
Future<void> _cancelDiscovery() async {
await _streamSubscription?.cancel();
setState(() => _isDiscovering = false);
}
Future<void> _discoveryOnListen(BluetoothDiscoveryResult event) async {
setState(() {
final int existingIndex =
_discoveryResults.indexWhere((element) => element.device.address == event.device.address);
if (existingIndex >= 0) {
_discoveryResults[existingIndex] = BluetoothObject(event);
} else {
_discoveryResults.add(BluetoothObject(event));
}
});
}
Future<void> _restartDiscovery() async {
_discoveryResults.clear();
if (_hasActiveObject) {
_discoveryResults.add(_activeObject!);
}
if (_streamSubscription != null) {
await _cancelDiscovery();
}
await _startDiscovery();
}
Future<void> _toggleConnection(BluetoothObject bluetoothObject) async {
await _cancelDiscovery();
if (_hasActiveObject) {
_activeObject!.disconnectDevice();
_activeObject = bluetoothObject;
} else {
_activeObject = bluetoothObject;
}
_activeObject!.isConnected ? await bluetoothObject.disconnectDevice() : await bluetoothObject.connectDevice();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: _isDiscovering ? const Text('Bluetooth Devices (searching...)') : const Text('Bluetooth Devices'),
actions: <Widget>[
_isDiscovering
? FittedBox(
child: Container(
margin: const EdgeInsets.all(16.0),
child: const CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
)
: IconButton(
icon: const Icon(Icons.replay),
onPressed: _restartDiscovery,
)
],
),
body: Center(
child: ListView(children: [
SingleChildScrollView(
physics: const ScrollPhysics(),
child: Column(
children: <Widget>[
SingleSection(
title: "Setup",
children: [
CustomListTile(
title: _enabledBluetooth ? "Bluetooth Enabled" : "Please Enable Bluetooth (click me)",
icon: _enabledBluetooth ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded,
onTap: () async {
await _enableBluetooth();
},
),
CustomListTile(
title: _enabledPermissions ? "Permissions Granted" : "Please Grant Permissions (click me)",
icon: _enabledPermissions ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded,
onTap: () async {
await _enablePermissions();
},
),
CustomListTile(
title: "App Settings (if permissions denied)",
icon: Icons.settings,
onTap: () async {
await AppSettings.openAppSettings();
},
),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
// const EdgeInsets defaultContentPadding = EdgeInsets.symmetric(horizontal: 16.0);
Expanded(
child: ElevatedButton(
onPressed: () async {
/*await Permission.bluetoothConnect.request();
await Permission.bluetoothScan.request();
if (await Permission.bluetoothScan.request().isGranted) {
// Either the permission was already granted before or the user just granted it.
debugPrint("Location Permission is granted");
} else {
debugPrint("Location Permission is denied.");
}*/
},
style: buttonStyle,
child: const Text("Get BT Permissions"),
),
),
const SizedBox(width: 16.0),
Expanded(
child: ElevatedButton(
onPressed: () async {
_restartDiscovery();
},
style: buttonStyle,
child: const Text("Restart Scan"),
),
)
]),
),
const Divider(),
SingleSection(
title: "Bluetooth Devices",
children: [
const CustomListTile(
title: "short Press for connect / disconnect",
icon: Icons.info_outline_rounded,
),
const CustomListTile(
title: "long press for pair / unpair",
icon: Icons.info_outline_rounded,
),
const Divider(),
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: _discoveryResults.length,
itemBuilder: (BuildContext context, index) {
BluetoothObject bluetoothObject = _discoveryResults[index];
final device = bluetoothObject.device;
return BluetoothDeviceEntry(
settingsPage: BluetoothDeviceSettings(bluetoothObject: bluetoothObject),
context: context,
device: device,
bluetoothObject: bluetoothObject,
onTap: () async {
await _toggleConnection(bluetoothObject);
},
onLongPress: () async {
await bluetoothObject.bondDevice();
},
);
},
),
],
)
],
),
),
]),
),
//drawer: const Sidebar(),
);
}
}