Skip to content

Conversation

@Brazol
Copy link
Contributor

@Brazol Brazol commented Dec 11, 2025

Summary by CodeRabbit

  • New Features

    • Added Picture-in-Picture priority setting to prefer camera or screen share (with fallback).
  • Improvements

    • Consolidated PiP configuration for simpler setup across platforms.
    • More stable, deterministic participant ordering and filtering.
    • Improved handling and rendering when participants are screen-sharing; PiP respects configured priorities.

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

@Brazol Brazol requested a review from a team as a code owner December 11, 2025 21:50
@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

Walkthrough

Adds a PiP track-priority option and threads a consolidated PictureInPictureConfiguration through iOS/Android PiP views; introduces CallParticipantsSortingMixin and refactors participant selection/rendering to use computed sortedParticipants and screenShareParticipant.

Changes

Cohort / File(s) Summary
Changelog & Configuration
packages/stream_video_flutter/CHANGELOG.md, packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/picture_in_picture_configuration.dart
Added PipTrackPriority enum and pipTrackPriority field to PictureInPictureConfiguration; documented behavior and updated changelog.
Call participants sorting mixin & export
packages/stream_video_flutter/lib/src/call_participants/call_participants_sorting_mixin.dart, packages/stream_video_flutter/lib/stream_video_flutter.dart
New CallParticipantsSortingMixin providing stable ordering, filtering/sorting hooks, recalculateParticipants, screenShareParticipant detection; mixin exported.
Call participants UI
packages/stream_video_flutter/lib/src/call_participants/call_participants.dart
Reworked to use CallParticipantsSortingMixin; removed internal participant lists, wired subscriptions to recompute flow, and render via sortedParticipants / screenShareParticipant.
iOS PiP view (UiKit)
packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_ui_kit_view.dart
Replaced legacy per-field params with pictureInPictureConfiguration; added pipParticipant/priorityTrack logic, updated track subscription/toggling and deprecated old fields.
Android PiP overlay & view
packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart, packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_android_view.dart
AndroidPipOverlay now accepts pictureInPictureConfiguration (keeps deprecated sort/customBuilder fallbacks); selection of screen-share vs camera content honors configuration; view updated to pass full configuration.
PiP wiring in call content & livestream
packages/stream_video_flutter/lib/src/call_screen/call_content/call_content.dart, packages/stream_video_flutter/lib/src/livestream/livestream_content.dart
Updated iOS PiP initialization sites to pass consolidated pictureInPictureConfiguration instead of separate fields.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to:
    • pipTrackPriority, shouldShowScreenShare, and priorityTrack logic in stream_picture_in_picture_ui_kit_view.dart and android_pip_overlay.dart.
    • CallParticipantsSortingMixin.recalculateParticipants for stable ordering, filter/sort correctness, and safe setState usage.
    • Deprecated constructor/field handling and migration guidance.
    • Lifecycle/subscription changes and track toggling on app lifecycle events.

Possibly related PRs

Suggested reviewers

  • xsahil03x
  • renefloor

Poem

🐇 I hopped in with a twitch and a blink,
Camera or share — the config will think.
I sort all my friends in a neat little stack,
Pick the right view and never look back. 🎥🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is entirely missing. The template requires sections for Goal, Implementation details, UI Changes, Testing, and Contributor Checklist, but none were provided. Add a complete pull request description following the template, including Goal, Implementation details, UI Changes with screenshots/videos, Testing strategy, and Contributor Checklist items.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding an option to prioritize which track (screen share or camera) displays in Picture in Picture mode.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/option-to-prioritize-camera-feed

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b899510 and 9cec233.

📒 Files selected for processing (2)
  • packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (2 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (2 hunks)
⏰ 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). (8)
  • GitHub Check: analyze
  • GitHub Check: build
  • GitHub Check: analyze_legacy_version
  • GitHub Check: stream_video_noise_cancellation
  • GitHub Check: stream_video_flutter
  • GitHub Check: stream_video
  • GitHub Check: iOS Build (Dogfooding)
  • GitHub Check: stream_video_push_notification
🔇 Additional comments (15)
packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (6)

112-114: Clean mixin integration for sorting logic.

The CallParticipantsSortingMixin integration centralizes participant sorting and filtering, and the subscription field is properly typed for lifecycle management.


116-120: LGTM!

The getter overrides correctly expose widget properties to the mixin.


125-133: LGTM!

Initial participants are properly seeded, and the subscription is conditionally set up only when using call state rather than explicit participants.


136-140: LGTM!

The dispose method properly cancels the subscription to prevent memory leaks.


161-161: Good fix for initial state seeding!

This addresses the past review comment about missing initial state when the call changes.


167-179: LGTM!

The build method cleanly integrates with the mixin's computed properties (screenShareParticipant and sortedParticipants), and the non-null assertion at line 171 is safe after the null check at line 167.

packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (9)

1-2: LGTM: Appropriate use of ignore directive.

The ignore directive is correctly scoped to allow the class to reference its own deprecated fields (sort and customBuilder) while maintaining backward compatibility.


3-7: LGTM: Import added for screen share content.

The new import for ScreenShareContent is required for the PiP track priority feature and is properly placed.


12-25: LGTM: Well-structured backward compatibility approach.

The constructor properly introduces the new pictureInPictureConfiguration while maintaining deprecated parameters with clear migration guidance. This allows gradual adoption without breaking existing code.


40-49: LGTM: Clean mixin integration with backward compatibility.

The CallParticipantsSortingMixin integration is well-structured, with the participantSort getter properly falling back to the deprecated sort field when the new configuration is not provided.


51-59: LGTM: Previous issue resolved - initial participants now calculated.

Line 54 properly seeds sortedParticipants with the initial state before subscribing to changes, addressing the concern from the previous review about empty participants on first build.


61-65: LGTM: Proper resource cleanup.

The dispose method correctly cancels the subscription to prevent memory leaks.


69-76: LGTM: Proper customBuilder resolution with fallback chain.

The customBuilder resolution correctly prioritizes the new configuration over the deprecated field, maintaining backward compatibility.


112-118: LGTM: Correct rendering hierarchy.

The return statement properly prioritizes the custom builder over the default PiP content, with an appropriate Material wrapper for the Android PiP overlay.


78-110: LGTM: Previous logic error fixed!

Line 78 now correctly checks customBuilder == null before building the default PiP content. This resolves the critical issue from the previous review where pipBody was built but never used.

The content selection logic properly handles screen share availability and priority, with appropriate widget keys for state management. The default behavior is already well-documented in PictureInPictureConfiguration: screen share is the explicit default priority, with clear fallback logic when camera is prioritized but disabled.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1b3dfc6 and e17a783.

📒 Files selected for processing (8)
  • packages/stream_video_flutter/CHANGELOG.md (1 hunks)
  • packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (3 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/call_content.dart (1 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (2 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/picture_in_picture_configuration.dart (2 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_android_view.dart (1 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_ui_kit_view.dart (3 hunks)
  • packages/stream_video_flutter/lib/src/livestream/livestream_content.dart (1 hunks)
⏰ 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). (8)
  • GitHub Check: analyze_legacy_version
  • GitHub Check: stream_video_screen_sharing
  • GitHub Check: stream_video_push_notification
  • GitHub Check: stream_video_noise_cancellation
  • GitHub Check: stream_video
  • GitHub Check: stream_video_flutter
  • GitHub Check: analyze
  • GitHub Check: build
🔇 Additional comments (12)
packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_android_view.dart (1)

154-156: LGTM!

The prioritiseScreenSharingTrack parameter is correctly propagated from the configuration to the AndroidPipOverlay widget, maintaining consistency with the iOS implementation path.

packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (2)

13-19: LGTM!

The new prioritiseScreenSharingTrack parameter is properly declared with a sensible default (true) for backward compatibility. The public field is correctly exposed.


37-39: LGTM!

The parameter is correctly propagated to StreamCallParticipants via the prioritiseScreenSharingInPictureInPicture parameter.

packages/stream_video_flutter/CHANGELOG.md (1)

1-5: LGTM!

The changelog entry is clear and accurately describes the new prioritiseScreenSharingTrack parameter behavior. The "Upcoming" section is appropriately used for unreleased changes.

packages/stream_video_flutter/lib/src/livestream/livestream_content.dart (1)

187-190: LGTM!

The prioritiseScreenSharingTrack parameter is correctly propagated to StreamPictureInPictureUiKitView for the iOS PiP path, consistent with the implementation in call_content.dart.

packages/stream_video_flutter/lib/src/call_screen/call_content/call_content.dart (1)

149-152: LGTM!

The prioritiseScreenSharingTrack parameter is correctly propagated to StreamPictureInPictureUiKitView for the iOS PiP path, aligning with the Android implementation in StreamPictureInPictureAndroidView.

packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/picture_in_picture_configuration.dart (1)

17-17: LGTM! Clear documentation and sensible default.

The new configuration option is well-documented, explaining both the prioritization behavior and the fallback logic when video is disabled. The default value of true is reasonable for maintaining existing behavior.

Also applies to: 29-37

packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (1)

206-234: PiP prioritization logic is correct.

The implementation correctly handles the prioritization behavior:

  1. When prioritiseScreenSharingInPictureInPicture is true and screen share is available → displays screen share
  2. When false and screen share is available but video is enabled → displays camera feed
  3. Fallback: when video is disabled but screen share is available → displays screen share regardless of the flag

The logic aligns with the documented behavior.

packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_ui_kit_view.dart (4)

44-44: LGTM! Parameter declaration is consistent.

The parameter naming prioritiseScreenSharingTrack matches the configuration field in PictureInPictureConfiguration, maintaining consistency in the iOS PiP implementation.

Also applies to: 50-50


84-100: Priority logic correctly implemented for iOS PiP.

The prioritization logic matches the Android implementation in call_participants.dart, ensuring consistent behavior across platforms. The priorityTrack is correctly set to screen share when prioritized or as fallback, otherwise defaults to video.


102-121: Track subscription logic handles prioritization correctly.

The code properly retrieves the appropriate track based on priorityTrack and updates subscriptions when needed. The early return ensures the platform channel isn't invoked until the track is available.


123-143: Platform channel payload correctly reflects prioritization.

The participant data sent to the iOS native layer properly uses pipParticipant and correctly sets isVideoEnabled to include the shouldShowScreenShare condition (line 134), ensuring the platform UI accurately reflects when screen sharing is being displayed.

@codecov
Copy link

codecov bot commented Dec 11, 2025

Codecov Report

❌ Patch coverage is 0% with 135 lines in your changes missing coverage. Please review.
✅ Project coverage is 6.29%. Comparing base (0126924) to head (9cec233).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...picture/stream_picture_in_picture_ui_kit_view.dart 0.00% 56 Missing ⚠️
...ontent/picture_in_picture/android_pip_overlay.dart 0.00% 31 Missing ⚠️
..._participants/call_participants_sorting_mixin.dart 0.00% 29 Missing ⚠️
...r/lib/src/call_participants/call_participants.dart 0.00% 16 Missing ⚠️
...lib/src/call_screen/call_content/call_content.dart 0.00% 1 Missing ⚠️
...icture/stream_picture_in_picture_android_view.dart 0.00% 1 Missing ⚠️
...flutter/lib/src/livestream/livestream_content.dart 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main   #1127      +/-   ##
========================================
- Coverage   6.30%   6.29%   -0.01%     
========================================
  Files        593     594       +1     
  Lines      40894   40948      +54     
========================================
  Hits        2578    2578              
- Misses     38316   38370      +54     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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: 3

Caution

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

⚠️ Outside diff range comments (1)
packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (1)

116-159: Missing subscription disposal in dispose() method.

The _participantsSubscription is created in initState() and conditionally cancelled in didUpdateWidget(), but there's no dispose() override to cancel the subscription when the widget is removed from the tree. This could cause a memory leak.

Add a dispose method:

+  @override
+  void dispose() {
+    _participantsSubscription?.cancel();
+    super.dispose();
+  }
+
   @override
   Widget build(BuildContext context) {
🧹 Nitpick comments (5)
packages/stream_video_flutter/lib/src/call_participants/call_participants_sorting_mixin.dart (3)

18-27: Provide default implementations for participantFilter / participantSort to avoid forced overrides
Right now these are abstract getters, so every consumer mixing this in must implement both (even if they just want “no filter / default sort”). Consider defaulting them to null in the mixin.

-  Filter<CallParticipantState>? get participantFilter;
+  Filter<CallParticipantState>? get participantFilter => null;

-  Sort<CallParticipantState>? get participantSort;
+  Sort<CallParticipantState>? get participantSort => null;

30-75: Avoid O(n²) indexOf hot-path in stable ordering
_sortedParticipantKeys.indexOf(...) is used in (1) the “add new keys” loop and (2) the sort comparator, which can get costly with frequent updates. Consider building a Map<String,int> once per recalculation.

   void recalculateParticipants(List<CallParticipantState> newParticipants) {
     final participants = [
       ...newParticipants,
     ].where(participantFilter ?? (_) => true).toList();

+    final keyIndex = <String, int>{
+      for (var i = 0; i < _sortedParticipantKeys.length; i++)
+        _sortedParticipantKeys[i]: i,
+    };
+
     for (final participant in participants) {
-      final index = _sortedParticipantKeys.indexOf(
-        participant.uniqueParticipantKey,
-      );
-      if (index == -1) {
+      if (!keyIndex.containsKey(participant.uniqueParticipantKey)) {
         _sortedParticipantKeys.add(participant.uniqueParticipantKey);
+        keyIndex[participant.uniqueParticipantKey] = _sortedParticipantKeys.length - 1;
       }
     }

     // First apply previous sorting on new participants list
     participants.sort(
-      (a, b) => _sortedParticipantKeys
-          .indexOf(a.uniqueParticipantKey)
-          .compareTo(_sortedParticipantKeys.indexOf(b.uniqueParticipantKey)),
+      (a, b) =>
+          (keyIndex[a.uniqueParticipantKey] ?? 1 << 30)
+              .compareTo(keyIndex[b.uniqueParticipantKey] ?? 1 << 30),
     );

78-80: Consider clearing _participants / _screenShareParticipant when clearing cache (if callers expect a full reset)
As-is, clearParticipantSortingCache() only resets ordering keys; UI will keep rendering the last _participants until the next recalculateParticipants(). If “reset” semantics are desired, clear the rest too.

packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_ui_kit_view.dart (2)

124-137: Subscription update condition should match the chosen priorityTrack
Right now you may call updateSubscription(... trackType: priorityTrack ...) even when the other track is what’s enabled (because the condition is (isVideoEnabled || isScreenShareEnabled)). Consider tightening to the selected track’s enabled flag to reduce unnecessary updates / surprising behavior.

-      if (videoTrack == null &&
-          (pipParticipant.isVideoEnabled ||
-              pipParticipant.isScreenShareEnabled)) {
+      final shouldSubscribePriorityTrack = priorityTrack == SfuTrackType.video
+          ? pipParticipant.isVideoEnabled
+          : pipParticipant.isScreenShareEnabled;
+
+      if (videoTrack == null && shouldSubscribePriorityTrack) {
         await widget.call.updateSubscription(
           userId: pipParticipant.userId,
           sessionId: pipParticipant.sessionId,
           trackIdPrefix: pipParticipant.trackIdPrefix,
           trackType: priorityTrack,
           videoDimension: RtcVideoDimensionPresets.h360_169,
         );

189-199: Consider unawaited(...) (or error handling) for _handleParticipantsChange in the listener
Since _handleParticipantsChange is async and you’re calling it from listen(...) without awaiting, it’s easy to drop errors / create overlapping calls. You already import dart:async, so making it explicit helps.

-          _handleParticipantsChange(
+          unawaited(_handleParticipantsChange(
             state.callParticipants,
             (widget
                         .pictureInPictureConfiguration
                         ?.iOSPiPConfiguration
                         .includeLocalParticipantVideo ??
                     widget.configuration?.includeLocalParticipantVideo ??
                     true) &&
                 widget.call.state.value.iOSMultitaskingCameraAccessEnabled,
-          );
+          ));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e17a783 and baf568b.

📒 Files selected for processing (9)
  • packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (4 hunks)
  • packages/stream_video_flutter/lib/src/call_participants/call_participants_sorting_mixin.dart (1 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/call_content.dart (1 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (2 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/picture_in_picture_configuration.dart (2 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_android_view.dart (1 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_ui_kit_view.dart (6 hunks)
  • packages/stream_video_flutter/lib/src/livestream/livestream_content.dart (1 hunks)
  • packages/stream_video_flutter/lib/stream_video_flutter.dart (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/stream_video_flutter/lib/src/livestream/livestream_content.dart
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_android_view.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). (8)
  • GitHub Check: analyze
  • GitHub Check: stream_video_push_notification
  • GitHub Check: stream_video
  • GitHub Check: stream_video_screen_sharing
  • GitHub Check: build
  • GitHub Check: stream_video_flutter
  • GitHub Check: stream_video_noise_cancellation
  • GitHub Check: analyze_legacy_version
🔇 Additional comments (11)
packages/stream_video_flutter/lib/stream_video_flutter.dart (1)

30-30: LGTM!

The export is correctly positioned alphabetically and appropriately exposes the new mixin for consumers who need custom participant sorting behavior.

packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/picture_in_picture_configuration.dart (2)

12-12: Good use of enum for track priority.

Using an enum instead of a boolean (as suggested in past review feedback) provides better clarity and extensibility for future priority options.


31-40: Clear and thorough documentation.

The fallback behavior for camera priority is well-documented, making the feature's behavior predictable for consumers.

packages/stream_video_flutter/lib/src/call_screen/call_content/call_content.dart (1)

144-148: LGTM!

The iOS PiP initialization correctly uses the consolidated pictureInPictureConfiguration object, aligning with the unified configuration approach across platforms.

packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (3)

77-101: Logic correctly implements track priority with fallback.

The implementation properly handles:

  • Screen share priority: shows screen share when available
  • Camera priority: shows camera, but falls back to screen share when video is disabled

The conditional logic is sound and aligns with the documented behavior in PictureInPictureConfiguration.


104-109: Good backward compatibility with deprecated parameters.

The fallback chain correctly prefers the new pictureInPictureConfiguration while maintaining support for deprecated customBuilder parameter during the transition period.


44-45: No action required.

The participantFilter returning null is intentionally handled by CallParticipantsSortingMixin using a null coalescing operator (participantFilter ?? (_) => true), which treats null as "include all participants." This is safe, correct, and the expected design.

packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/stream_picture_in_picture_ui_kit_view.dart (4)

41-63: Good API migration pattern (assert + deprecations), but document precedence when both configs are provided
Code clearly prefers pictureInPictureConfiguration over deprecated fields; consider adding a short doc note in the constructor dartdoc about precedence when both are non-null.


91-98: Sort precedence looks correct
pictureInPictureConfiguration?.sort ?? participantSort ?? speaker is a sensible migration path and keeps old behavior available.


140-171: Config fallback wiring to iOSPiPConfiguration looks consistent
The pictureInPictureConfiguration?.iOSPiPConfiguration ?? configuration fallback reads clean and keeps deprecation compatibility.


281-303: Nice: screen-share tracks are now toggled alongside video tracks
This aligns the bandwidth-saving logic with the new PiP track-priority behavior.

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)
packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (1)

114-155: Fix subscription lifecycle: handle participants→null transition and cancel in dispose

Two issues around _participantsSubscription:

  1. No subscription when switching from custom participants back to call-driven state

    In didUpdateWidget, when you go from oldWidget.participants != null to widget.participants == null and widget.call == oldWidget.call, neither branch executes. That means:

    • No subscription is created.
    • The widget stops reacting to callParticipants updates once the custom list is removed.

    You can fix this by treating “participants became null” similarly to the “call changed” case and (re)subscribing:

    @override
    void didUpdateWidget(covariant StreamCallParticipants oldWidget) {
      super.didUpdateWidget(oldWidget);
    
      if (widget.participants != null) {
        _participantsSubscription?.cancel();
    
        if (!const ListEquality<CallParticipantState>().equals(
          widget.participants!.toList(),
          oldWidget.participants?.toList(),
        )) {
          recalculateParticipants(widget.participants!);
        }
  • } else if (widget.call != oldWidget.call) {
  •  _participantsSubscription?.cancel();
    
  •  _participantsSubscription = widget.call
    
  •      .partialState((state) => state.callParticipants)
    
  •      .listen(recalculateParticipants);
    
  • } else {
  •  final participantsBecameNull = oldWidget.participants != null;
    
  •  final callChanged = widget.call != oldWidget.call;
    
  •  if (participantsBecameNull || callChanged) {
    
  •    _participantsSubscription?.cancel();
    
  •    _participantsSubscription = widget.call
    
  •        .partialState((state) => state.callParticipants)
    
  •        .listen(recalculateParticipants);
    
  •  }
    
    }
    }
    
    If `partialState` does *not* synchronously emit the current value, you may also want to trigger an immediate `recalculateParticipants(widget.call.state.value.callParticipants)` in that branch.
    
    
  1. No cleanup in dispose

    _participantsSubscription is canceled in some didUpdateWidget paths but never in dispose, so a lingering subscription could outlive the widget if nothing else triggers the cancellation.

    Add a dispose override:

    @override
    void didUpdateWidget(covariant StreamCallParticipants oldWidget) {
      super.didUpdateWidget(oldWidget);
      ...
    }
  • @OverRide
  • void dispose() {
  • _participantsSubscription?.cancel();
  • super.dispose();
  • }
    
    

Together these changes ensure the widget remains in sync when toggling between external and call-driven participants and avoids leaking subscriptions.

🧹 Nitpick comments (1)
packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (1)

51-59: Consider handling configuration changes in didUpdateWidget.

If the call or pictureInPictureConfiguration changes after initial build, the current implementation won't:

  1. Recreate the subscription for a different call
  2. Recalculate participants with the new sort order
+  @override
+  void didUpdateWidget(covariant AndroidPipOverlay oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    if (oldWidget.call != widget.call) {
+      _participantsSubscription?.cancel();
+      _participantsSubscription = widget.call
+          .partialState((state) => state.callParticipants)
+          .listen(recalculateParticipants);
+      recalculateParticipants(widget.call.state.value.callParticipants);
+    } else if (oldWidget.pictureInPictureConfiguration?.sort != 
+               widget.pictureInPictureConfiguration?.sort ||
+               oldWidget.sort != widget.sort) {
+      recalculateParticipants(widget.call.state.value.callParticipants);
+    }
+  }
+
   @override
   void dispose() {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between baf568b and e716a1c.

📒 Files selected for processing (2)
  • packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (2 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (2 hunks)
⏰ 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). (9)
  • GitHub Check: stream_video_push_notification
  • GitHub Check: stream_video
  • GitHub Check: stream_video_screen_sharing
  • GitHub Check: stream_video_flutter
  • GitHub Check: stream_video_noise_cancellation
  • GitHub Check: build
  • GitHub Check: analyze_legacy_version
  • GitHub Check: analyze
  • GitHub Check: iOS Build (Dogfooding)
🔇 Additional comments (4)
packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (2)

78-85: Verify default behavior when no configuration is provided.

When pictureInPictureConfiguration is null, the condition pipTrackPriority != PipTrackPriority.camera evaluates to true (since null != PipTrackPriority.camera), meaning screen share is prioritized by default.

Is this the intended default behavior for backward compatibility? Consider adding explicit handling:

+      // Default to screen share priority when not configured (backward compatibility)
       final shouldShowScreenShare =
           hasScreenShare &&
-          (widget.pictureInPictureConfiguration?.pipTrackPriority !=
-                  PipTrackPriority.camera ||
+          (widget.pictureInPictureConfiguration?.pipTrackPriority ==
+                  PipTrackPriority.screenShare ||
+              widget.pictureInPictureConfiguration?.pipTrackPriority == null ||
               !pipParticipant.isVideoEnabled);

Alternatively, if camera should be the default, update accordingly.


105-115: LGTM! Proper fallback chain for custom builder.

The resolution correctly prioritizes the new configuration's builder, falls back to the deprecated customBuilder, and finally uses the computed pipBody. This maintains backward compatibility while supporting the new configuration approach.

packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (2)

112-121: Good use of mixin + clear wiring of filter/sort into the shared logic

CallParticipantsSortingMixin integration and the participantFilter / participantSort getters cleanly delegate configuration from the widget to the shared sorting/filtering logic. This keeps the state class thin and makes future reuse of the mixin straightforward.


159-172: Build logic cleanly delegates to screen‑share vs regular layouts using computed state

The build method now branches on screenShareParticipant and consistently feeds sortedParticipants into both ScreenShareCallParticipantsContent and RegularCallParticipantsContent. This centralizes participant ordering/filtering in the mixin and keeps UI components focused on layout, which is a nice simplification.

…tStream/stream-video-flutter into feat/option-to-prioritize-camera-feed
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: 2

🧹 Nitpick comments (1)
packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (1)

40-65: Consider handling call or sort changes in didUpdateWidget.

If the call or pictureInPictureConfiguration props can change during the widget's lifetime, the current implementation won't update the subscription or re-seed participants. For a PiP overlay that typically exists for one call session, this may be acceptable, but worth noting.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e716a1c and b899510.

📒 Files selected for processing (2)
  • packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (2 hunks)
  • packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (2 hunks)
⏰ 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). (8)
  • GitHub Check: analyze_legacy_version
  • GitHub Check: stream_video
  • GitHub Check: stream_video_noise_cancellation
  • GitHub Check: stream_video_flutter
  • GitHub Check: stream_video_push_notification
  • GitHub Check: analyze
  • GitHub Check: build
  • GitHub Check: iOS Build (Dogfooding)
🔇 Additional comments (6)
packages/stream_video_flutter/lib/src/call_participants/call_participants.dart (4)

112-120: Clean mixin integration.

The state class properly integrates CallParticipantsSortingMixin with getter overrides that delegate to widget properties. This pattern cleanly separates sorting/filtering concerns.


122-134: Proper initial state seeding and subscription setup.

The initState correctly calls recalculateParticipants with the initial state before subscribing to updates, avoiding the empty-state-on-first-build issue. The conditional subscription logic appropriately skips streaming when external participants are provided.


136-140: LGTM!

Proper cleanup of the subscription in dispose.


163-183: LGTM!

The build method cleanly selects between screen-share and regular content based on the computed screenShareParticipant, and consistently uses sortedParticipants from the mixin.

packages/stream_video_flutter/lib/src/call_screen/call_content/picture_in_picture/android_pip_overlay.dart (2)

12-35: Good deprecation pattern with migration guidance.

The deprecated annotations with clear migration messages help users transition to PictureInPictureConfiguration. The dual deprecation (on parameter and field) ensures visibility in IDE warnings.


51-59: Initial state seeding addressed.

The initState now correctly calls recalculateParticipants with the initial participant state before subscribing, addressing the previously flagged issue.

@Brazol Brazol merged commit 8cdb13b into main Dec 16, 2025
12 of 14 checks passed
@Brazol Brazol deleted the feat/option-to-prioritize-camera-feed branch December 16, 2025 10:22
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.

3 participants