Refactor Bluetooth availability handling in ScannerScreen and AppDrawer

- Removed the tooltip for Bluetooth availability from ScannerScreen and integrated the BleAvailabilityIcon directly into the AppDrawer.
- Updated the AppDrawer to accept Bluetooth availability state and a callback for state changes.
- Simplified button labels in ScannerScreen and ScannedDevicesPlaceholderWidget for clarity.
- Adjusted padding for buttons to enhance UI consistency.
This commit is contained in:
Foti Dim
2026-02-09 21:47:50 +01:00
parent b49d54b900
commit 485c0808ad
4 changed files with 43 additions and 112 deletions
+9 -34
View File
@@ -7,7 +7,6 @@ import 'package:universal_ble/universal_ble.dart';
import 'package:universal_ble_example/data/company_identifier_service.dart';
import 'package:universal_ble_example/data/mock_universal_ble.dart';
import 'package:universal_ble_example/data/scan_controller.dart';
import 'package:universal_ble_example/home/widgets/ble_availability_icon.dart';
import 'package:universal_ble_example/home/widgets/drawer.dart';
import 'package:universal_ble_example/home/widgets/scan_filter_widget.dart';
import 'package:universal_ble_example/home/widgets/scanned_devices_placeholder_widget.dart';
@@ -370,25 +369,6 @@ class _ScannerScreenState extends State<ScannerScreen> {
}
}
String _getBluetoothAvailabilityTooltip() {
switch (bleAvailabilityState) {
case AvailabilityState.poweredOn:
return 'Bluetooth is on';
case AvailabilityState.poweredOff:
return 'Bluetooth is off';
case AvailabilityState.resetting:
return 'Bluetooth is resetting';
case AvailabilityState.unauthorized:
return 'Bluetooth permission denied';
case AvailabilityState.unsupported:
return 'Bluetooth not supported';
case AvailabilityState.unknown:
return 'Bluetooth status unknown';
case null:
return 'Checking Bluetooth status...';
}
}
@override
void setState(VoidCallback fn) {
if (mounted) super.setState(fn);
@@ -429,6 +409,13 @@ class _ScannerScreenState extends State<ScannerScreen> {
UniversalBle.queueType = _queueType;
});
},
availabilityState: bleAvailabilityState,
onAvailabilityStateChanged: (state) {
setState(() => bleAvailabilityState = state);
if (state == AvailabilityState.poweredOn) {
_tryAutoStartScan();
}
},
),
appBar: AppBar(
title: Row(
@@ -477,18 +464,6 @@ class _ScannerScreenState extends State<ScannerScreen> {
),
),
),
const SizedBox(width: 12),
Tooltip(
message: _getBluetoothAvailabilityTooltip(),
triggerMode: TooltipTriggerMode.tap,
child: BleAvailabilityIcon(onAvailabilityStateChanged: (state) {
setState(() => bleAvailabilityState = state);
// Auto-start scanning when Bluetooth becomes available
if (state == AvailabilityState.poweredOn) {
_tryAutoStartScan();
}
}),
),
],
),
elevation: 0,
@@ -519,7 +494,7 @@ class _ScannerScreenState extends State<ScannerScreen> {
size: 20,
),
label: Text(
_scanController?.isScanning == true ? 'Stop Scan' : 'Start Scan',
_scanController?.isScanning == true ? 'Stop' : 'Scan',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
@@ -535,7 +510,7 @@ class _ScannerScreenState extends State<ScannerScreen> {
? colorScheme.onError
: colorScheme.onPrimary,
padding: const EdgeInsets.symmetric(
horizontal: 16,
horizontal: 12,
vertical: 12,
),
shape: RoundedRectangleBorder(
+30
View File
@@ -2,6 +2,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:universal_ble/universal_ble.dart';
import 'package:universal_ble_example/home/widgets/ble_availability_icon.dart';
import 'package:universal_ble_example/home/scanner_screen.dart';
import 'package:universal_ble_example/home/system_devices_screen.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -9,11 +10,15 @@ import 'package:url_launcher/url_launcher.dart';
class AppDrawer extends StatefulWidget {
final QueueType? queueType;
final Function(QueueType)? onQueueTypeChanged;
final AvailabilityState? availabilityState;
final void Function(AvailabilityState)? onAvailabilityStateChanged;
const AppDrawer({
super.key,
this.queueType,
this.onQueueTypeChanged,
this.availabilityState,
this.onAvailabilityStateChanged,
});
@override
@@ -62,6 +67,14 @@ class _AppDrawerState extends State<AppDrawer> {
),
),
),
if (widget.availabilityState != null &&
widget.onAvailabilityStateChanged != null)
ListTile(
leading: BleAvailabilityIcon(
onAvailabilityStateChanged: widget.onAvailabilityStateChanged!,
),
title: Text(_getBluetoothAvailabilityLabel(widget.availabilityState!)),
),
ListTile(
leading: const Icon(Icons.search),
title: const Text('Scanner'),
@@ -180,6 +193,23 @@ class _AppDrawerState extends State<AppDrawer> {
);
}
String _getBluetoothAvailabilityLabel(AvailabilityState state) {
switch (state) {
case AvailabilityState.poweredOn:
return 'Bluetooth is on';
case AvailabilityState.poweredOff:
return 'Bluetooth is off';
case AvailabilityState.resetting:
return 'Bluetooth is resetting';
case AvailabilityState.unauthorized:
return 'Bluetooth permission denied';
case AvailabilityState.unsupported:
return 'Bluetooth not supported';
case AvailabilityState.unknown:
return 'Bluetooth state unknown';
}
}
String _getQueueTypeLabel(QueueType queueType) {
switch (queueType) {
case QueueType.global:
@@ -52,7 +52,7 @@ class ScannedDevicesPlaceholderWidget extends StatelessWidget {
onPressed: onTap,
icon: const Icon(Icons.play_arrow, size: 24),
label: const Text(
'Start Scan',
'Scan',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
@@ -62,7 +62,7 @@ class ScannedDevicesPlaceholderWidget extends StatelessWidget {
backgroundColor: colorScheme.primary,
foregroundColor: colorScheme.onPrimary,
padding: const EdgeInsets.symmetric(
horizontal: 32,
horizontal: 12,
vertical: 16,
),
shape: RoundedRectangleBorder(
@@ -36,7 +36,6 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
List<BleService> discoveredServices = [];
final List<String> _logs = [];
final binaryCode = TextEditingController();
bool _isLoading = false;
bool _isDiscoveringServices = false;
bool _isDeviceInfoExpanded = false;
bool _isDeviceActionsExpanded = true;
@@ -407,18 +406,11 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
Future<T> Function() action, {
Function(dynamic error)? onError,
}) async {
setState(() {
_isLoading = true;
});
try {
return await action();
} catch (e) {
onError?.call(e);
rethrow;
} finally {
setState(() {
_isLoading = false;
});
}
}
@@ -560,7 +552,7 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
size: 20,
),
label: Text(
_scanController!.isScanning ? 'Stop Scan' : 'Start Scan',
_scanController!.isScanning ? 'Stop' : 'Scan',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
@@ -574,7 +566,7 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
? colorScheme.onError
: colorScheme.onPrimary,
padding: const EdgeInsets.symmetric(
horizontal: 16,
horizontal: 12,
vertical: 12,
),
shape: RoundedRectangleBorder(
@@ -584,72 +576,6 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
),
),
),
Visibility.maintain(
visible: _isLoading,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
colorScheme.primary,
),
),
),
),
),
if (deviceType != DeviceType.desktop)
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
isConnected
? Icons.bluetooth_connected
: Icons.bluetooth_disabled,
color: isConnected
? Colors.green
: colorScheme.onSurface.withValues(alpha: 0.6),
size: 20,
),
)
else
Container(
margin: const EdgeInsets.only(right: 16),
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: isConnected
? Colors.green.withValues(alpha: 0.2)
: Colors.orange.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isConnected
? Icons.check_circle
: Icons.bluetooth_disabled,
size: 16,
color: isConnected
? Colors.green.shade700
: Colors.orange.shade700,
),
const SizedBox(width: 6),
Text(
isConnected ? 'Connected' : 'Disconnected',
style: TextStyle(
color: isConnected
? Colors.green.shade700
: Colors.orange.shade700,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
body: Row(