From 30b25784c1929a461bb8e4efa0c1ff22b67335a5 Mon Sep 17 00:00:00 2001 From: Yandrik Date: Tue, 5 May 2026 22:01:22 +0200 Subject: [PATCH] fix: lifecycle --- .../bootloader_recovery_update_page.dart | 10 +++++-- lib/pages/device_details_page.dart | 30 ++++++++++++++++--- lib/pages/devices_tab_page.dart | 29 +++++++++++------- 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/lib/pages/bootloader_recovery_update_page.dart b/lib/pages/bootloader_recovery_update_page.dart index 79f6138..91e776c 100644 --- a/lib/pages/bootloader_recovery_update_page.dart +++ b/lib/pages/bootloader_recovery_update_page.dart @@ -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 _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; } diff --git a/lib/pages/device_details_page.dart b/lib/pages/device_details_page.dart index e042f90..c02bef5 100644 --- a/lib/pages/device_details_page.dart +++ b/lib/pages/device_details_page.dart @@ -66,6 +66,7 @@ class _DeviceDetailsPageState extends ConsumerState { bool _isPairingCheckRunning = false; ProviderSubscription>? _connectionStatusSubscription; + BluetoothController? _bluetooth; ShifterService? _shifterService; StreamSubscription? _statusSubscription; @@ -146,7 +147,10 @@ class _DeviceDetailsPageState extends ConsumerState { '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 { super.dispose(); } - Future _disconnectOnClose() async { + Future _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 { _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 { 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 { } else { bluetooth = await ref.read(bluetoothProvider.future); } + _bluetooth = bluetooth; if (!isCurrentDeviceConnected(bluetooth)) { return; } @@ -264,6 +281,9 @@ class _DeviceDetailsPageState extends ConsumerState { } 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 { if (bluetooth == null) { return null; } + _bluetooth = bluetooth; final service = FirmwareUpdateService( transport: ShifterFirmwareUpdateTransport( @@ -722,6 +743,7 @@ class _DeviceDetailsPageState extends ConsumerState { try { final bluetooth = await ref.read(bluetoothProvider.future); + _bluetooth = bluetooth; final result = await bluetooth.connectById( widget.deviceAddress, timeout: const Duration(seconds: 10), diff --git a/lib/pages/devices_tab_page.dart b/lib/pages/devices_tab_page.dart index 7b141e3..d53b0c1 100644 --- a/lib/pages/devices_tab_page.dart +++ b/lib/pages/devices_tab_page.dart @@ -26,8 +26,10 @@ class _DevicesTabPageState extends ConsumerState { static const Duration _bootloaderScanTimeout = Duration(seconds: 10); StreamSubscription>? _scanSubscription; + BluetoothController? _bluetooth; DiscoveredDevice? _dfuDevice; bool _isBootloaderScanStarting = false; + bool _isDisposed = false; @override void initState() { @@ -37,8 +39,8 @@ class _DevicesTabPageState extends ConsumerState { @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 { 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 { } Future _stopBootloaderScan([BluetoothController? bluetooth]) async { - await _scanSubscription?.cancel(); + final subscription = _scanSubscription; _scanSubscription = null; + await subscription?.cancel(); await bluetooth?.stopScan(); } void _updateBootloaderDevice(List devices) { + if (_isDisposed || !mounted) { + return; + } + final dfuDevice = devices.cast().firstWhere( (device) => device != null && _isBootloaderAdvertisement(device), orElse: () => null, ); - if (!mounted) { - return; - } if (dfuDevice == null) { _clearBootloaderDevice(); @@ -104,7 +113,7 @@ class _DevicesTabPageState extends ConsumerState { } void _clearBootloaderDevice() { - if (!mounted || _dfuDevice == null) { + if (_isDisposed || !mounted || _dfuDevice == null) { return; } @@ -139,12 +148,12 @@ class _DevicesTabPageState extends ConsumerState { builder: (_) => _BootloaderRecoverySetupPage(device: device), ), ); - if (!mounted || firmware == null) { + if (!mounted || _isDisposed || firmware == null) { return; } await _stopBootloaderScan(); - if (!mounted) { + if (!mounted || _isDisposed) { return; } _clearBootloaderDevice();