Skip to content

Conversation

@xsahil03x
Copy link
Member

@xsahil03x xsahil03x commented Feb 3, 2026

Core PR: GetStream/stream-core-flutter#43

Description of the pull request

This PR introduces a complete redesign of the Avatar components to simplify the API, improve consistency, and align with the new design system.

The following components have been removed and replaced:

  • StreamUserAvatar
  • StreamGroupAvatar
  • StreamChannelAvatar

They are replaced by a new suite of components:

  • StreamUserAvatar: Displays a single user's avatar.
  • StreamUserAvatarGroup: Displays a grid of user avatars for group channels.
  • StreamUserAvatarStack: A new component for displaying a stack of overlapping user avatars.
  • StreamChannelAvatar: Rebuilt to internally use the new avatar components.

Key changes:

  • Simplified Sizing: The constraints parameter has been replaced with a size enum (e.g., StreamAvatarSize.lg) for standardized sizing.
  • Tap Handling: The onTap callback has been removed. Avatars should now be wrapped with a GestureDetector or InkWell for handling interactions.
  • Parameter Cleanup: Parameters like borderRadius, selected, selectionColor, and selectionThickness have been removed to simplify the API. Customization is now handled through theming.
  • Online Indicator: The showOnlineStatus parameter has been renamed to showOnlineIndicator. Its default is now true for StreamUserAvatar and false for avatars inside groups or stacks.
  • StreamGroupAvatar is now StreamUserAvatarGroup: The component has been renamed and now accepts a list of User objects instead of Member objects.
  • Migration Guide: A detailed migration guide for the new avatar components has been added under migrations/redesign/stream_avatar.md.
  • Theming: Integrated the new StreamTheme extension from stream_core_flutter for better theme management.

Screenshots / Videos

Before After
Screenshot 2026-02-03 at 07 24 07 Screenshot 2026-02-03 at 07 24 07

Summary by CodeRabbit

  • New Features

    • Redesigned avatar system with size-based enums (xs, sm, md, lg, xl) and new components: StreamUserAvatar, StreamUserAvatarGroup, StreamUserAvatarStack, redesigned StreamChannelAvatar.
  • Breaking Changes

    • Constraint-based avatar sizing removed; avatar APIs now use size and showOnlineIndicator flags.
    • Legacy avatar widgets/export paths removed and replaced by redesigned components.
  • Documentation

    • Added migration guide with component-specific before/after examples.

This commit introduces a complete redesign of the Avatar components to simplify the API, improve consistency, and align with the new design system.

The following components have been removed and replaced:
- `StreamUserAvatar`
- `StreamGroupAvatar`
- `StreamChannelAvatar`

They are replaced by a new suite of components:
- `StreamUserAvatar`: Displays a single user's avatar.
- `StreamUserAvatarGroup`: Displays a grid of user avatars for group channels.
- `StreamUserAvatarStack`: A new component for displaying a stack of overlapping user avatars.
- `StreamChannelAvatar`: Rebuilt to internally use the new avatar components.

Key changes:
- **Simplified Sizing**: The `constraints` parameter has been replaced with a `size` enum (e.g., `StreamAvatarSize.lg`) for standardized sizing.
- **Tap Handling**: The `onTap` callback has been removed. Avatars should now be wrapped with a `GestureDetector` or `InkWell` for handling interactions.
- **Parameter Cleanup**: Parameters like `borderRadius`, `selected`, `selectionColor`, and `selectionThickness` have been removed to simplify the API. Customization is now handled through theming.
- **Online Indicator**: The `showOnlineStatus` parameter has been renamed to `showOnlineIndicator`. Its default is now `true` for `StreamUserAvatar` and `false` for avatars inside groups or stacks.
- **`StreamGroupAvatar` is now `StreamUserAvatarGroup`**: The component has been renamed and now accepts a list of `User` objects instead of `Member` objects.
- **Migration Guide**: A detailed migration guide for the new avatar components has been added under `migrations/redesign/stream_avatar.md`.
- **Theming**: Integrated the new `StreamTheme` extension from `stream_core_flutter` for better theme management.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 3, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review
📝 Walkthrough

Walkthrough

This PR replaces legacy avatar widgets with redesigned, component-based avatar implementations (size-based API), updates usages across the SDK and sample app, adds migration docs, introduces a git dependency on stream_core_flutter, and adjusts CI/workflow and map marker sizing to enum-based MarkerSize.

Changes

Cohort / File(s) Summary
Config & Workflows
analysis_options.yaml, melos.yaml, packages/stream_chat_flutter/pubspec.yaml, .github/workflows/*
Disables one lint rule entry, adds git dependency for stream_core_flutter (specific ref/path), and sets GIT_LFS_SKIP_SMUDGE: 1 in multiple CI workflows.
Migration Docs
migrations/redesign/README.md, migrations/redesign/stream_avatar.md
Adds migration guides documenting API changes (constraints → size enums, removed onTap/styling args, new Stack/Group components) and a migration checklist.
Removed legacy avatars
packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart, .../group_avatar.dart, .../channel/stream_channel_avatar.dart
Removes old avatar implementations and related typedefs/exports.
New avatar components
packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar.dart, stream_user_avatar_group.dart, stream_user_avatar_stack.dart, stream_channel_avatar.dart
Adds new StreamUserAvatar, StreamUserAvatarGroup, StreamUserAvatarStack, and a new StreamChannelAvatar component using size enums and new composition patterns.
Public exports updated
packages/stream_chat_flutter/lib/stream_chat_flutter.dart
Exports new avatar components and re-exports StreamAvatarSize/StreamTheme; removes old avatar exports.
SDK UI callsites
packages/stream_chat_flutter/lib/src/** (channel_, dialog_, gallery_, message_, reactions, poll, scroll_view, etc.)
Updates ~50 UI files to use new avatar API: replace BoxConstraints/showOnlineStatus with size and showOnlineIndicator, wrap avatars in GestureDetector for tap handling, and replace manual avatar stacking with the new stack/group components.
Thread/Message logic adjustment
message_widget/thread_participants.dart, bottom_row.dart, thread_participants.dart, poll_options_list_view.dart
Removes theme-dependent manual stacking; uses StreamUserAvatarStack/Group and simplifies constructors/fields.
Sample app updates
sample_app/lib/**
Updates sample app to new avatar sizes and MarkerSize usage (map marker API changes), plus ThemeData -> StreamTheme extension usage.
Map/marker refactor
sample_app/lib/widgets/location/*, simple_map_view.dart
Introduces MarkerSize enum, switches marker APIs to use enum/value, and updates SimpleMapView typedefs/fields accordingly.
Tests
packages/stream_chat_flutter/test/src/**
Adjusts tests to assert on new avatar components (StreamUserAvatarGroup) and user-based inputs; updates golden/config usages for CI goldens.
Tests config
packages/stream_chat_flutter/test/flutter_test_config.dart
Adds ciGoldensConfig to AlchemistConfig usage.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • renefloor
  • Brazol

Poem

🐰
Hop, hop — avatars dressed in sizes new and bright,
From xs to xl we tumble into light.
Stacks and groups now line the meadow way,
I nibble docs and cheer the fresh display. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: a complete redesign of Avatar components, which is the central focus of all file modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/new-stream-avatar

Important

Action Needed: IP Allowlist Update

If your organization protects your Git platform with IP whitelisting, please add the new CodeRabbit IP address to your allowlist:

  • 136.113.208.247/32 (new)
  • 34.170.211.100/32
  • 35.222.179.152/32

Failure to add the new IP will result in interrupted reviews.


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.

This commit updates the `stream_core_flutter` dependency to use a version from a git repository.

Specifically, it points to the `feat/avatar-group-and-badge-count` branch of the `GetStream/stream-core-flutter` repository.
@xsahil03x
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 3, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@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_chat_flutter/lib/src/poll/interactor/poll_options_list_view.dart (1)

223-249: ⚠️ Potential issue | 🟡 Minor

Remove or use the unused radius parameter.

The radius field in OptionVoters accepts a value but is never used in the build method. The avatar size is hard-coded to .xs via StreamUserAvatarStack. Since the only instantiation in the codebase doesn't pass a custom radius, either remove the parameter or map it to control the avatar size.

🤖 Fix all issues with AI agents
In `@analysis_options.yaml`:
- Around line 47-49: Fix the minor grammar in the comment above the disabled
lint rule `avoid_redundant_argument_values`: change "Does not always makes sense
to remove them, Also makes it hard to notice future breaking changes if any." to
a grammatically correct sentence such as "Does not always make sense to remove
them; it also makes it hard to notice future breaking changes." so the comment
reads clearly and correctly.

In
`@packages/stream_chat_flutter/lib/src/components/avatar/stream_channel_avatar.dart`:
- Around line 83-86: The builder in StreamChannelAvatar currently maps members
with members.map((it) => it.user!) which can throw on null users; change the
mapping to filter out null Member.user values (e.g., members.where((m) => m.user
!= null).map((m) => m.user!)) and ensure StreamUserAvatarGroup receives a
non-null list (handle the empty list case by passing an empty list or a fallback
user list) so the UI never crashes when some members have partial payloads.

In
`@packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar_stack.dart`:
- Around line 66-74: The constructor docs for StreamUserAvatarStack promise "The
[users] list must not be empty" but there's no enforcement; update the API to
enforce this invariant by changing the field and constructor parameter from
Iterable<User> to List<User> and adding assert(users.isNotEmpty, 'users must not
be empty') in the StreamUserAvatarStack constructor (and update all call sites),
or if you cannot change the type, add a runtime check in the build() method (or
constructor) that verifies the iterable is non-empty (e.g., by checking
.isNotEmpty on a collection or using iterator.moveNext()) and throw/assert with
the same message.
🧹 Nitpick comments (4)
packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar_group.dart (1)

50-58: Consider adding assertion for non-empty users list.

The documentation at line 53 states "The [users] list must not be empty", but there's no runtime assertion to enforce this. Consider adding an assertion in the constructor or in the build method to catch misuse early in development.

💡 Suggested assertion
   const StreamUserAvatarGroup({
     super.key,
     required this.users,
     this.size,
-  });
+  }) : assert(users.isNotEmpty, 'users must not be empty');

Note: Since users is an Iterable, calling isNotEmpty in an assertion may iterate the collection. If this is a concern, you could defer the check to the build method or remove the documentation claim.

packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar.dart (1)

99-104: Consider a stable hash if avatar colors must persist consistently across app sessions.
String.hashCode is deterministic within a single app session but not guaranteed stable across program executions. The current code achieves within-session color consistency for the same user ID. However, if you need avatar colors to remain consistent when users close and reopen the app, use a stable hash function instead.

🔧 Suggested change (stable hash)
-    final userHash = user.id.hashCode; // Ensure deterministic colors.
+    final userHash = _stableHash(user.id); // Ensure deterministic colors.
     final colorPair = avatarPalette[userHash % avatarPalette.length];
@@
   StreamOnlineIndicatorSize _indicatorSizeForAvatarSize(
     StreamAvatarSize size,
   ) => switch (size) {
     .xs || .sm => StreamOnlineIndicatorSize.sm,
     .md => StreamOnlineIndicatorSize.md,
     .lg => StreamOnlineIndicatorSize.lg,
     .xl => StreamOnlineIndicatorSize.xl,
   };
+
+  static int _stableHash(String value) {
+    var hash = 0;
+    for (final unit in value.codeUnits) {
+      hash = 31 * hash + unit;
+    }
+    return hash & 0x7fffffff;
+  }
 }
packages/stream_chat_flutter/lib/src/dialogs/channel_info_dialog.dart (1)

52-65: Consider extracting the repeated member lookup.

The firstWhere call with the same predicate is executed twice—once for the avatar (lines 52-56) and once for the name (lines 60-65). Extracting this to a local variable would improve readability and avoid redundant iteration.

♻️ Suggested refactor
           if (channel.isDistinct && channel.memberCount == 2)
             Column(
               children: [
+                Builder(
+                  builder: (context) {
+                    final otherMember = members
+                        .firstWhere((e) => e.user?.id != userAsMember.user?.id);
+                    return Column(
+                      children: [
+                        StreamUserAvatar(
+                          size: .xl,
+                          user: otherMember.user!,
+                        ),
+                        const SizedBox(height: 6),
+                        Text(
+                          otherMember.user?.name ?? '',
+                          style: StreamChatTheme.of(context).textTheme.footnoteBold,
+                          maxLines: 1,
+                          overflow: TextOverflow.ellipsis,
+                        ),
+                      ],
+                    );
+                  },
+                ),
-                StreamUserAvatar(
-                  size: .xl,
-                  user: members
-                      .firstWhere(
-                        (e) => e.user?.id != userAsMember.user?.id,
-                      )
-                      .user!,
-                ),
-                const SizedBox(height: 6),
-                Text(
-                  members
-                          .firstWhere(
-                            (e) => e.user?.id != userAsMember.user?.id,
-                          )
-                          .user
-                          ?.name ??
-                      '',
-                  style: StreamChatTheme.of(context).textTheme.footnoteBold,
-                  maxLines: 1,
-                  overflow: TextOverflow.ellipsis,
-                ),
               ],
             ),
sample_app/lib/pages/new_group_chat_screen.dart (1)

145-148: Consider hiding the online indicator for selected members.

Per the PR objectives, showOnlineIndicator defaults to true for StreamUserAvatar. In this context (displaying selected group members), showing the online status may add visual noise. Consider explicitly setting showOnlineIndicator: false if the online indicator is not desired here.

♻️ Suggested change
                                         StreamUserAvatar(
                                           size: .xl,
                                           user: user,
+                                          showOnlineIndicator: false,
                                         ),

xsahil03x and others added 3 commits February 3, 2026 07:50
Temporarily skip LFS smudge process on CI workflows to work around an issue with a dependency.
@codecov
Copy link

codecov bot commented Feb 3, 2026

Codecov Report

❌ Patch coverage is 83.49515% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.89%. Comparing base (a767e3a) to head (617f132).

Files with missing lines Patch % Lines
...ottom_sheets/stream_channel_info_bottom_sheet.dart 0.00% 4 Missing ⚠️
...er/lib/src/message_widget/thread_participants.dart 0.00% 2 Missing ⚠️
.../lib/src/message_widget/user_avatar_transform.dart 66.66% 2 Missing ⚠️
...b/src/components/avatar/stream_channel_avatar.dart 95.23% 1 Missing ⚠️
.../lib/src/components/avatar/stream_user_avatar.dart 96.77% 1 Missing ⚠️
...hat_flutter/lib/src/message_widget/bottom_row.dart 0.00% 1 Missing ⚠️
.../channel_scroll_view/stream_channel_grid_tile.dart 0.00% 1 Missing ⚠️
.../channel_scroll_view/stream_channel_list_tile.dart 0.00% 1 Missing ⚠️
...h_scroll_view/stream_message_search_list_tile.dart 0.00% 1 Missing ⚠️
...l_view/user_scroll_view/stream_user_grid_tile.dart 0.00% 1 Missing ⚠️
... and 2 more
Additional details and impacted files
@@             Coverage Diff             @@
##           v10.0.0    #2502      +/-   ##
===========================================
+ Coverage    66.85%   66.89%   +0.03%     
===========================================
  Files          430      431       +1     
  Lines        26365    26188     -177     
===========================================
- Hits         17627    17518     -109     
+ Misses        8738     8670      -68     

☔ 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.

@xsahil03x
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 3, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In
`@packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar_stack.dart`:
- Around line 99-112: The build method of StreamUserAvatarStack currently always
constructs a StreamAvatarStack with children from users, but the docstring says
it should return an empty SizedBox when users is empty; update build to check
users.isEmpty and return SizedBox.shrink() (or SizedBox()) when true, otherwise
map users to StreamUserAvatar as before; ensure you reference the existing
symbols (build, users, StreamAvatarStack, StreamUserAvatar, max, size, overlap)
and keep the existing mapping logic for the non-empty case.
🧹 Nitpick comments (2)
.github/workflows/release_publish.yml (1)

10-12: Reasonable temporary workaround; consider tracking the TODO with an issue.

The GIT_LFS_SKIP_SMUDGE env var is appropriate for avoiding unnecessary LFS downloads when using git-based dependencies. The TODO comment is good practice. However, workflow-level TODOs can be easily overlooked once the PR is merged.

Consider creating a tracking issue linked to the stream_core_flutter publication milestone to ensure this env block gets removed, preventing it from becoming permanent technical debt.

packages/stream_chat_flutter/lib/src/components/avatar/stream_user_avatar_stack.dart (1)

84-89: Consider validating overlap bounds.

The documentation specifies overlap should be between 0.0 and 1.0, but there's no assertion preventing invalid values. Negative overlap or values exceeding 1.0 could produce unexpected layout behavior.

🛡️ Proposed assertion
   const StreamUserAvatarStack({
     super.key,
     required this.users,
     this.size,
     this.overlap = 0.33,
     this.max = 5,
-  }) : assert(max >= 2, 'max must be at least 2');
+  }) : assert(max >= 2, 'max must be at least 2'),
+       assert(overlap >= 0.0 && overlap <= 1.0, 'overlap must be between 0.0 and 1.0');

The migration guide for the redesigned UI components is updated to include a new "Theming" section. This section explains how to use `StreamTheme` to customize the appearance of the new components, providing code examples for both light and dark modes. It also adds a "Need Help?" section directing users to open a GitHub issue if they encounter problems.
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