Skip to content

Conversation

@hiroshihorie
Copy link
Member

@hiroshihorie hiroshihorie commented Jan 26, 2026

  • Clear selected device when audio/video is disabled
  • Reset selection if the device disappears on device change
  • Prevents DropdownButton “exactly one item with value” crash

Summary by CodeRabbit

  • Bug Fixes
    • Reset prejoin device selection when a previously chosen audio or video device becomes unavailable.
    • Keep device selection and preview tracks synchronized: auto-select a device when enabling audio/video if none is selected, and stop/clear preview tracks when disabling or when inputs are removed.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 26, 2026

📝 Walkthrough

Walkthrough

Reset stale audio/video selections when device lists change; stop and clear tracks and selections when audio/video are disabled; on enabling, auto-select the first available device and start its track if none is selected.

Changes

Cohort / File(s) Summary
Patch annotation
\.changes/example-prejoin-dropdown
Adds a patch entry marking a fix for example prejoin device dropdown handling (annotation only).
Prejoin page device logic
example/lib/pages/prejoin.dart
Sync selected devices with available inputs; clear stale _selectedAudioDevice/_selectedVideoDevice when inputs change; stop/clear _audioTrack/_videoTrack when disabling or when inputs empty; auto-select first available device and start track when enabling and none selected.

Sequence Diagram(s)

sequenceDiagram
  participant UI as Prejoin UI
  participant DM as DeviceManager
  participant TM as TrackManager
  UI->>DM: request device list (_loadDevices)
  DM-->>UI: return audio/video inputs
  alt device list changed / missing selection
    UI->>UI: clear stale _selectedAudioDevice/_selectedVideoDevice
    UI->>TM: stop & nullify corresponding tracks
  end
  opt enabling audio/video with available inputs
    UI->>UI: auto-select first device if none
    UI->>TM: start track for selected device
    TM-->>UI: track started
  end
  opt disabling audio/video
    UI->>TM: stop & clear track
    UI->>UI: clear selected device
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through lists of sound and sight,
Cleared stale picks in the pale moonlight,
When muted, I pause and rest,
When unmuted, I pick the best,
First-found device makes preview bright. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Fix prejoin device dropdown crashes in example app' directly and accurately summarizes the main objective of the changeset, which is to fix dropdown-related crashes by synchronizing device selection state.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f2ec16 and e619993.

📒 Files selected for processing (1)
  • example/lib/pages/prejoin.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Build for Flutter Linux
  • GitHub Check: Build for Flutter Web WASM
  • GitHub Check: Build for Flutter Web
  • GitHub Check: Build for Flutter Windows
  • GitHub Check: Build for Flutter macOS
  • GitHub Check: Build for Flutter Android
🔇 Additional comments (3)
example/lib/pages/prejoin.dart (3)

126-137: LGTM: disable clears selection and enable defaults when needed
This cleanup/auto-select flow should help prevent stale dropdown values and track leaks.


142-153: LGTM: audio enable/disable cleanup looks consistent
The symmetry with video handling is good and aligns with the crash prevention goal.


87-123: No changes needed. The current implementation using contains() is correct because MediaDevice properly overrides the equality operator to compare by deviceId, kind, label, and groupId—not by identity. Device selections will persist correctly across device list refreshes.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
example/lib/pages/prejoin.dart (1)

83-112: Guard auto‑selection when audio/video is disabled to avoid DropdownButton crashes.
_loadDevices can set _selectedAudioDevice / _selectedVideoDevice even when _enableAudio / _enableVideo is false (e.g., initial load with prefs disabled or device-change events). That leaves a non‑null value while items are [], reintroducing the “exactly one item with value” assertion.

🔧 Proposed fix
-    if (_audioInputs.isNotEmpty) {
+    if (_enableAudio && _audioInputs.isNotEmpty) {
       if (_selectedAudioDevice == null) {
         _selectedAudioDevice = _audioInputs.first;
         Future.delayed(const Duration(milliseconds: 100), () async {
           await _changeLocalAudioTrack();
           setState(() {});
         });
       }
     }

-    if (_videoInputs.isNotEmpty) {
+    if (_enableVideo && _videoInputs.isNotEmpty) {
       if (_selectedVideoDevice == null) {
         _selectedVideoDevice = _videoInputs.first;
         Future.delayed(const Duration(milliseconds: 100), () async {
           await _changeLocalVideoTrack();
           setState(() {});
         });
       }
     }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7d9fac and 60dab35.

📒 Files selected for processing (2)
  • .changes/example-prejoin-dropdown
  • example/lib/pages/prejoin.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build for Flutter Linux
  • GitHub Check: Build for Flutter Windows
  • GitHub Check: Build for Flutter Web WASM
  • GitHub Check: Build for Flutter iOS
  • GitHub Check: Build for Flutter Web
  • GitHub Check: Build for Flutter macOS
  • GitHub Check: Build for Flutter Android
🔇 Additional comments (3)
.changes/example-prejoin-dropdown (1)

1-1: Changelog entry looks good.

example/lib/pages/prejoin.dart (2)

116-127: Good: clearing selection on disable and re-selecting on enable.
This aligns the dropdown value with enabled state.


132-143: Good: audio toggle now keeps selection consistent.
Clearing on disable and re‑selecting on enable is the right behavior.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
example/lib/pages/prejoin.dart (1)

87-112: Stop and clear tracks when no devices remain after a device-change event.

If a device disappears and the list becomes empty, the selection is cleared but the existing track can remain active, leaving preview/join state referencing a stale device. Stopping and clearing the track in that case keeps state consistent.

🛠️ Proposed fix
    if (_selectedAudioDevice != null && !_audioInputs.contains(_selectedAudioDevice)) {
      _selectedAudioDevice = null;
    }
+   if (_selectedAudioDevice == null && _audioInputs.isEmpty) {
+     await _audioTrack?.stop();
+     _audioTrack = null;
+   }
    if (_selectedVideoDevice != null && !_videoInputs.contains(_selectedVideoDevice)) {
      _selectedVideoDevice = null;
    }
+   if (_selectedVideoDevice == null && _videoInputs.isEmpty) {
+     await _videoTrack?.stop();
+     _videoTrack = null;
+   }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60dab35 and 1b24412.

📒 Files selected for processing (1)
  • example/lib/pages/prejoin.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build for Flutter Linux
  • GitHub Check: Build for Flutter Web
  • GitHub Check: Build for Flutter Web WASM
  • GitHub Check: Build for Flutter macOS
  • GitHub Check: Build for Flutter Android
  • GitHub Check: Build for Flutter Windows
  • GitHub Check: Build for Flutter iOS
🔇 Additional comments (2)
example/lib/pages/prejoin.dart (2)

119-126: LGTM: video toggle now keeps selection and track state aligned.


135-142: LGTM: audio toggle now keeps selection and track state aligned.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
example/lib/pages/prejoin.dart (1)

102-121: Guard async callbacks with mounted before calling setState.

_loadDevices awaits async work and schedules delayed callbacks; if the widget is disposed in between, setState will throw. Add mounted checks to avoid post-dispose updates.

🔧 Suggested fix
-        Future.delayed(const Duration(milliseconds: 100), () async {
-          await _changeLocalAudioTrack();
-          setState(() {});
-        });
+        Future.delayed(const Duration(milliseconds: 100), () async {
+          if (!mounted) return;
+          await _changeLocalAudioTrack();
+          if (mounted) setState(() {});
+        });
       }
     }
...
-        Future.delayed(const Duration(milliseconds: 100), () async {
-          await _changeLocalVideoTrack();
-          setState(() {});
-        });
+        Future.delayed(const Duration(milliseconds: 100), () async {
+          if (!mounted) return;
+          await _changeLocalVideoTrack();
+          if (mounted) setState(() {});
+        });
       }
     }
-    setState(() {});
+    if (mounted) setState(() {});
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b24412 and 4f2ec16.

📒 Files selected for processing (1)
  • example/lib/pages/prejoin.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build for Flutter macOS
  • GitHub Check: Build for Flutter Linux
  • GitHub Check: Build for Flutter Web WASM
  • GitHub Check: Build for Flutter iOS
  • GitHub Check: Build for Flutter Web
  • GitHub Check: Build for Flutter Windows
  • GitHub Check: Build for Flutter Android
🔇 Additional comments (1)
example/lib/pages/prejoin.dart (1)

87-96: No changes needed—MediaDevice already implements value-based equality.

MediaDevice overrides operator== to compare all four fields (deviceId, kind, label, groupId), so List.contains() works correctly. The original code is sound and does not require the suggested deviceId-matching refactor.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@hiroshihorie hiroshihorie merged commit db929a3 into main Jan 27, 2026
16 of 17 checks passed
@hiroshihorie hiroshihorie deleted the hiroshi/example-dropdown-crash branch January 27, 2026 08:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Example app: "There should be exactly one item with [DropdownButton's value: MediaDevicefdeviceld:, label:, kind: videoinput, groupld: )..."

3 participants