Skip to content

Bug: Firmware update dialog stacks multiple times on BLE reconnect #5566

@omi-discord-vector

Description

@omi-discord-vector

Bug

The firmware update popup appears multiple times layered on top of each other (reported ~15 stacked dialogs). User has to cancel each one individually.

Root Cause

File: app/lib/providers/device_provider.dart

The flow is:

  1. BLE device connects → _onDeviceConnected()_checkFirmwareUpdates()
  2. _checkFirmwareUpdates() calls checkFirmwareUpdates() (async network call with up to 3 retries), then calls showFirmwareUpdateDialog() after a 500ms delay
  3. showFirmwareUpdateDialog() calls Flutter showDialog() which stacks on the navigator — each call adds a new dialog layer

The problem: there is no guard against concurrent or repeated calls. When BLE connection flickers (connect/disconnect/reconnect rapidly — common with Bluetooth), _onDeviceConnected fires each time. Each invocation spawns an independent async chain that eventually shows its own dialog. There is no _isFirmwareDialogShowing flag or similar mutex.

Existing guards in showFirmwareUpdateDialog() check _havingNewFirmware, _isFirmwareUpdateInProgress, and _isOnFirmwareUpdatePage — but none of these prevent multiple dialogs from stacking since _havingNewFirmware stays true across all calls once the firmware check returns.

Similarly, _checkFirmwareUpdates checks _isFirmwareUpdateInProgress but has no _isCheckingFirmware lock, so multiple parallel checks can run.

Fix

Two guards needed:

  1. Add _isCheckingFirmware flag to _checkFirmwareUpdates() — return early if already checking:
bool _isCheckingFirmware = false;

void _checkFirmwareUpdates() async {
  if (_isFirmwareUpdateInProgress || _isCheckingFirmware) return;
  _isCheckingFirmware = true;
  try {
    await checkFirmwareUpdates();
    if (_havingNewFirmware) {
      Future.delayed(const Duration(milliseconds: 500), () {
        final context = MyApp.navigatorKey.currentContext;
        if (context != null) showFirmwareUpdateDialog(context);
      });
    }
  } finally {
    _isCheckingFirmware = false;
  }
}
  1. Add _isFirmwareDialogShowing flag to showFirmwareUpdateDialog() — prevent stacking:
bool _isFirmwareDialogShowing = false;

void showFirmwareUpdateDialog(BuildContext context) {
  if (_isFirmwareDialogShowing || !_havingNewFirmware || ...) return;
  _isFirmwareDialogShowing = true;
  
  showDialog(
    context: context,
    builder: ...,
  ).then((_) {
    _isFirmwareDialogShowing = false;
  });
}

Repro

  1. Have an Omi device with a firmware update available
  2. Be in an area with unstable BLE (or toggle Bluetooth rapidly)
  3. Each reconnect stacks another firmware update dialog

cc @BasedHardware/omi

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingflutterflutter workp2Priority: Important (score 14-21)ux-polishLayer: UI layout, animations, wording

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions