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(_firmwareProgressSubscription?.cancel());
unawaited(() async { unawaited(() async {
await service?.dispose(); await service?.dispose();
await _disconnectBootloaderIfStillConnected(bluetooth: bluetooth); await _disconnectBootloaderIfStillConnected(
bluetooth: bluetooth,
allowRefRead: false,
);
}()); }());
super.dispose(); super.dispose();
} }
Future<void> _disconnectBootloaderIfStillConnected({ Future<void> _disconnectBootloaderIfStillConnected({
BluetoothController? bluetooth, BluetoothController? bluetooth,
bool allowRefRead = true,
}) async { }) async {
bluetooth ??= ref.read(bluetoothProvider).valueOrNull; if (bluetooth == null && allowRefRead) {
bluetooth = ref.read(bluetoothProvider).valueOrNull;
}
if (bluetooth == null) { if (bluetooth == null) {
return; return;
} }

View File

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

View File

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