fix: lifecycle

This commit is contained in:
2026-05-05 22:01:22 +02:00
parent 0da0905697
commit 30b25784c1
3 changed files with 53 additions and 16 deletions

View File

@ -66,15 +66,21 @@ class _BootloaderRecoveryUpdatePageState
unawaited(_firmwareProgressSubscription?.cancel());
unawaited(() async {
await service?.dispose();
await _disconnectBootloaderIfStillConnected(bluetooth: bluetooth);
await _disconnectBootloaderIfStillConnected(
bluetooth: bluetooth,
allowRefRead: false,
);
}());
super.dispose();
}
Future<void> _disconnectBootloaderIfStillConnected({
BluetoothController? bluetooth,
bool allowRefRead = true,
}) async {
bluetooth ??= ref.read(bluetoothProvider).valueOrNull;
if (bluetooth == null && allowRefRead) {
bluetooth = ref.read(bluetoothProvider).valueOrNull;
}
if (bluetooth == null) {
return;
}

View File

@ -66,6 +66,7 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
bool _isPairingCheckRunning = false;
ProviderSubscription<AsyncValue<(ConnectionStatus, String?)>>?
_connectionStatusSubscription;
BluetoothController? _bluetooth;
ShifterService? _shifterService;
StreamSubscription<CentralStatus>? _statusSubscription;
@ -146,7 +147,10 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
'Disposing device details page for ${widget.deviceAddress}; '
'dfuState=${_dfuProgress.state}, isFirmwareUpdateBusy=$_isFirmwareUpdateBusy',
);
unawaited(_disconnectOnClose());
final bluetooth = _bluetooth;
unawaited(
_disconnectOnClose(bluetooth: bluetooth, allowRefRead: false),
);
_connectionStatusSubscription?.close();
_statusSubscription?.cancel();
_shifterService?.dispose();
@ -155,7 +159,10 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
super.dispose();
}
Future<void> _disconnectOnClose() async {
Future<void> _disconnectOnClose({
BluetoothController? bluetooth,
bool allowRefRead = true,
}) async {
if (_isFirmwareUpdateBusy) {
_log.info('Skipping disconnect on close because firmware update is busy');
return;
@ -169,10 +176,16 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
_hasRequestedDisconnect = true;
_isExitingPage = true;
final bluetoothController = bluetooth ??
_bluetooth ??
(allowRefRead ? ref.read(bluetoothProvider).value : null);
if (bluetoothController != null) {
_bluetooth = bluetoothController;
}
await _disposeFirmwareUpdateService();
final bluetooth = ref.read(bluetoothProvider).value;
await bluetooth?.disconnect();
await bluetoothController?.disconnect();
await _stopStatusStreaming();
}
@ -211,6 +224,9 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
if (_shifterService != null) {
final bluetooth = ref.read(bluetoothProvider).value;
if (bluetooth != null) {
_bluetooth = bluetooth;
}
if (bluetooth == null || !isCurrentDeviceConnected(bluetooth)) {
return;
}
@ -239,6 +255,7 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
} else {
bluetooth = await ref.read(bluetoothProvider.future);
}
_bluetooth = bluetooth;
if (!isCurrentDeviceConnected(bluetooth)) {
return;
}
@ -264,6 +281,9 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
}
final bluetooth = ref.read(bluetoothProvider).value;
if (bluetooth != null) {
_bluetooth = bluetooth;
}
if (bluetooth == null || !isCurrentDeviceConnected(bluetooth)) {
break;
}
@ -523,6 +543,7 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
if (bluetooth == null) {
return null;
}
_bluetooth = bluetooth;
final service = FirmwareUpdateService(
transport: ShifterFirmwareUpdateTransport(
@ -722,6 +743,7 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
try {
final bluetooth = await ref.read(bluetoothProvider.future);
_bluetooth = bluetooth;
final result = await bluetooth.connectById(
widget.deviceAddress,
timeout: const Duration(seconds: 10),

View File

@ -26,8 +26,10 @@ class _DevicesTabPageState extends ConsumerState<DevicesTabPage> {
static const Duration _bootloaderScanTimeout = Duration(seconds: 10);
StreamSubscription<List<DiscoveredDevice>>? _scanSubscription;
BluetoothController? _bluetooth;
DiscoveredDevice? _dfuDevice;
bool _isBootloaderScanStarting = false;
bool _isDisposed = false;
@override
void initState() {
@ -37,8 +39,8 @@ class _DevicesTabPageState extends ConsumerState<DevicesTabPage> {
@override
void dispose() {
final bluetooth = ref.read(bluetoothProvider).valueOrNull;
unawaited(_scanSubscription?.cancel());
_isDisposed = true;
final bluetooth = _bluetooth;
unawaited(_stopBootloaderScan(bluetooth));
super.dispose();
}
@ -52,14 +54,19 @@ class _DevicesTabPageState extends ConsumerState<DevicesTabPage> {
try {
final bluetooth = await ref.read(bluetoothProvider.future);
if (!mounted) {
if (!mounted || _isDisposed) {
return;
}
_bluetooth = bluetooth;
final scanResult = await bluetooth.startScan(
timeout: _bootloaderScanTimeout,
scanMode: ScanMode.lowLatency,
);
if (!mounted || _isDisposed) {
await bluetooth.stopScan();
return;
}
if (scanResult.isErr()) {
return;
}
@ -74,20 +81,22 @@ class _DevicesTabPageState extends ConsumerState<DevicesTabPage> {
}
Future<void> _stopBootloaderScan([BluetoothController? bluetooth]) async {
await _scanSubscription?.cancel();
final subscription = _scanSubscription;
_scanSubscription = null;
await subscription?.cancel();
await bluetooth?.stopScan();
}
void _updateBootloaderDevice(List<DiscoveredDevice> devices) {
if (_isDisposed || !mounted) {
return;
}
final dfuDevice = devices.cast<DiscoveredDevice?>().firstWhere(
(device) => device != null && _isBootloaderAdvertisement(device),
orElse: () => null,
);
if (!mounted) {
return;
}
if (dfuDevice == null) {
_clearBootloaderDevice();
@ -104,7 +113,7 @@ class _DevicesTabPageState extends ConsumerState<DevicesTabPage> {
}
void _clearBootloaderDevice() {
if (!mounted || _dfuDevice == null) {
if (_isDisposed || !mounted || _dfuDevice == null) {
return;
}
@ -139,12 +148,12 @@ class _DevicesTabPageState extends ConsumerState<DevicesTabPage> {
builder: (_) => _BootloaderRecoverySetupPage(device: device),
),
);
if (!mounted || firmware == null) {
if (!mounted || _isDisposed || firmware == null) {
return;
}
await _stopBootloaderScan();
if (!mounted) {
if (!mounted || _isDisposed) {
return;
}
_clearBootloaderDevice();