πŸ“‘ Device Manager & ADB Manager Dialog

March 29, 2026 Β· View on GitHub

← Back to README

Complete reference for device selection β€” how the system auto-detects devices, when and how the picker dialog appears, the IP connection flow, and the DeviceManager singleton API.


DeviceManager Singleton

The single source of truth for which ADB device to target. Every ADB command in the system builds its argument list using DeviceManager.instance.adbArgs.

State

FieldTypeDefaultMeaning
_deviceIdString?nullResolved target: serial, "ip:port", or "-d"
_needsDeviceSelectionboolfalseWhen true, the boot flow must show the dialog before starting

Properties

PropertyReturnDescription
deviceIdStringCurrent target; returns "-d" if nothing set
needsDeviceSelectionboolWhether user must pick a device
isUsbbool_deviceId == "-d"
isWifibool_deviceId is an IP address
adbArgsList<String>["-d"] or ["-s", _deviceId]

Key Methods

setFromArgs(List<String> args)

Called from the application entry point with CLI arguments:

args = ["--usb"]             β†’ _deviceId = "-d"
args = ["192.168.1.5"]       β†’ _deviceId = "192.168.1.5:5555"
args = ["192.168.1.5:5555"]  β†’ _deviceId = "192.168.1.5:5555"
args = []                    β†’ autoDetect()

autoDetect()

Calls AdbProvider.instance.getDevices() then:

ResultAction
0 devices_deviceId = "-d" Β· needsDeviceSelection = true
1 device (USB serial)_deviceId = "-d" Β· needsDeviceSelection = false
1 device (IP)_deviceId = "ip:port" Β· needsDeviceSelection = false
2+ devices_deviceId = first device Β· needsDeviceSelection = true

selectDevice(String id)

Called when the user picks a device in the dialog:

id = "emulator-5554"    β†’ _deviceId = "-d"    (USB/serial β†’ force USB flag)
id = "192.168.1.5:5555" β†’ _deviceId = "192.168.1.5:5555"  (keep IP)
needsDeviceSelection    β†’ false

When Does the Dialog Appear?

The dialog is shown in exactly two situations β€” never speculatively:

Situation 1 β€” Pre-Boot (no device or multiple devices)

App launched β†’ setFromArgs() β†’ autoDetect()
    β”‚
    └── needsDeviceSelection == true?
            β”‚
            β–Ό
       DeviceManagerDialog opens
            β”‚
       User picks device or types IP
            β”‚
       selectDevice(id) called
            β”‚
       Boot sequence begins

Situation 2 β€” Post-Error (connection failure during boot)

Boot sequence running...
    β”‚
    └── EXCEPTION thrown
            β”‚
            └── _isConnectionError(message)?
                    β”‚
                    β–Ό
        _canPickDevice = true
        Error panel shown in boot screen
        "Open ADB Manager" button appears
            β”‚
        User taps button β†’ dialog opens
            β”‚
        User picks device
            β”‚
        _resetBootState() β†’ _runBoot() (retry)

Key design decision: The dialog is never auto-popped during a connection error. The user sees the error message first, then deliberately opens the manager. This ensures they understand what failed before choosing how to fix it.


DeviceManagerDialog β€” UI Reference

Opened via: showDialog() with barrierColor: Colors.transparent

The transparent barrier means the InitScreen background (animated gradient) remains fully visible behind the dialog β€” no dark overlay.

Layout

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  πŸ“±  Android Dex β€” ADB Manager              β”‚
β”‚       Tap a device to connect instantly     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  ⚠ [Reason banner β€” why dialog opened]     β”‚  ← only shown if reason provided
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                             β”‚
β”‚  β”Œβ”€ DEVICE LIST (tap = immediate connect) ─┐ β”‚
β”‚  β”‚  πŸ”Œ  USB Device              β€Ί          β”‚ β”‚
β”‚  β”‚       ABC123XYZ β€” tap to connect        β”‚ β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚
β”‚  β”‚  πŸ“Ά  192.168.1.100:5555      β€Ί          β”‚ β”‚
β”‚  β”‚       Wi-Fi ADB β€” tap to connect        β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                                             β”‚
β”‚  [or "Scanning for ADB devices…" spinner]   β”‚
β”‚  [or "No ADB devices found" empty state]    β”‚
β”‚                                             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Connect via IP Address                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ 192.168.1.100          β”‚  β”‚  Connect  β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚  βœ— [Inline error if IP fails]              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  [ β†Ί Refresh Devices ]        [ βœ• Cancel ] β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Interaction Model

ActionResult
Tap any device rowNavigator.pop(deviceId) immediately β€” dialog closes
Type IP + press Connect / Enteradb connect ip:5555 runs; success β†’ Navigator.pop(ip)
IP connection failsInline error message replaces Connect spinner
Press RefreshRescans adb devices; list reloads with fade-in animation
Press CancelNavigator.pop(null) β€” dialog closes, no device set

IP Connection Flow (Detail)

User types "192.168.1.100" β†’ presses Connect
        β”‚
        β–Ό
ip = "192.168.1.100:5555"   (auto-appends port)

AdbProvider.run(["connect", "192.168.1.100:5555"])
        β”‚
        β”œβ”€ Output contains "connected" or "already connected"
        β”‚       β”‚
        β”‚       └── Navigator.pop("192.168.1.100:5555") βœ“
        β”‚           β†’ DeviceManager.selectDevice("192.168.1.100:5555")
        β”‚           β†’ Boot sequence starts / retries
        β”‚
        └─ Any other output / exception
                β”‚
                └── _ipError = "Unable to connect to 192.168.1.100:5555
                                β€” verify the IP and try again."
                    Spinner β†’ Connect button restored

_DeviceRow Widget

Each row in the device list. Hover-aware with animated border and arrow:

  • USB devices (no :) β†’ USB icon Β· label "USB Device" Β· sublabel "ABC123 β€” tap to connect"
  • Wi-Fi devices (contains :) β†’ Wi-Fi icon Β· label shows IP Β· sublabel "Wi-Fi ADB β€” tap to connect"
  • On hover β†’ blue border glows Β· arrow icon fades in
  • On tap β†’ immediate Navigator.pop(deviceId) β€” no additional confirm step

← Back to README Β· Boot Flow Β» Β· Modules Β»