-
Notifications
You must be signed in to change notification settings - Fork 373
fix(ui): use userId.hashCode for GradientAvatar colors #2381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This commit fixes an issue where GradientAvatars for users with same-length IDs would have identical colors. The `GradientAvatarPainter` now uses `Random(userId.hashCode)` instead of `Random(userId.length)` to generate colors and transform points. This ensures that different user IDs, even if they have the same length, will produce visually distinct avatars. A regression test (`gradient_avatar_issue_2369`) has been added to verify this fix, including the specific scenario reported in GitHub issue #2369. Additionally, the golden test theme in `flutter_test_config.dart` has been updated with a new background color, border color, name text style, and padding for improved visual clarity in golden tests.
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (20)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the WalkthroughConverted gradient avatar rendering to a deterministic, stateless implementation using userId.hashCode-based palette selection; added Jitter utility, PolygonCell and refactored PolygonGradientPainter; added String.initials, tests (golden and unit), test fakes for connectivity/path provider, and updated changelog for the avatar color bugfix. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor App
participant Widget as StreamGradientAvatar (Stateless)
participant Palette as _palettes
participant Jitter as Jitter (seeded)
participant Painter as PolygonGradientPainter
participant Canvas
App->>Widget: build(name, userId, jitterIntensity)
Widget->>Palette: select gradient (index = userId.hashCode % palettes.length)
Widget->>Jitter: Jitter(seed: userId.hashCode, intensity: jitterIntensity)
Widget->>Painter: paint(size, rows, cols, jitter, gradient)
Painter->>Jitter: sample jittered grid points
Painter->>Canvas: draw PolygonCell tiles with gradient
Canvas-->>Widget: background rendered
Widget->>Widget: overlay `_Initials` centered
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. 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. Comment |
There was a problem hiding this 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
🧹 Nitpick comments (5)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (4)
176-181: Guard nextInt() against zero/negative bounds for tiny avatar sizes.When size.width/height < grid counts,
~/can yield 0 causing RangeError in nextInt(0). Clamp to at least 1.- final dx = sign1 * 0.6 * _rand.nextInt(size.width ~/ columnCount); - final dy = sign2 * 0.6 * _rand.nextInt(size.height ~/ rowCount); + final dxRange = max(1, (size.width / columnCount).floor()); + final dyRange = max(1, (size.height / rowCount).floor()); + final dx = sign1 * 0.6 * _rand.nextInt(dxRange); + final dy = sign2 * 0.6 * _rand.nextInt(dyRange);
156-158: Repaint logic should depend on inputs; always-false can skip updates.If name/userId/font changes, the painter should repaint.
- bool shouldRepaint(covariant CustomPainter oldDelegate) => false; + bool shouldRepaint(covariant PolygonGradientPainter old) => + old.userId != userId || + old.username != username || + old.fontFamily != fontFamily;
123-126: Naming nit: smallerSide is actually the larger side.Minor readability fix.
- final smallerSide = size.width > size.height ? size.width : size.height; - final textSize = smallerSide / 3; + final longerSide = size.width > size.height ? size.width : size.height; + final textSize = longerSide / 3;
94-117: Micro-alloc/perf: avoid toList/indexOf in the inner loop.Not hot with 5x5, but trivial to tidy: maintain a Map<Offset,int> as you insert into
points, or build the full list once after the loops and compute indices from arithmetic instead ofindexOf.packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.dart (1)
188-228: Drop Expanded — redundant with Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly)Expanded is unnecessary here; return Padding directly as shown.
- children: users.map((userData) { + children: users.map((userData) { final (userId, userName) = userData; - return Expanded( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: spacing / 2), - child: _AvatarItem( - userId: userId, - userName: userName, - avatarSize: avatarSize, - ), - ), - ); + return Padding( + padding: EdgeInsets.symmetric(horizontal: spacing / 2), + child: _AvatarItem( + userId: userId, + userName: userName, + avatarSize: avatarSize, + ), + ); }).toList(),Verified repo SDK: ^3.6.2 — Dart 3 records are supported.
📜 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.
⛔ Files ignored due to path filters (4)
packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_3.pngis excluded by!**/*.png
📒 Files selected for processing (3)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart(3 hunks)packages/stream_chat_flutter/test/flutter_test_config.dart(2 hunks)packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.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). (10)
- GitHub Check: update_goldens
- GitHub Check: build (android)
- GitHub Check: test
- GitHub Check: analyze
- GitHub Check: stream_chat
- GitHub Check: stream_chat_flutter_core
- GitHub Check: stream_chat_persistence
- GitHub Check: stream_chat_flutter
- GitHub Check: stream_chat_localizations
- GitHub Check: analyze_legacy_versions
🔇 Additional comments (3)
packages/stream_chat_flutter/test/flutter_test_config.dart (2)
5-5: Import addition is correct.Required for Color/EdgeInsets/TextStyle usage in the theme.
16-25: Global golden theme change — confirm baseline updates and CI stability.This alters all golden snapshots. Ensure all affected baselines were regenerated and that CI (with PlatformGoldens disabled) still renders consistently on your goldens runner.
packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.dart (1)
124-178: Regression goldens for issue #2369 — LGTM.Good coverage: same-length numeric/alphabetic IDs, mixed lengths, and identical IDs.
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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.
⛔ Files ignored due to path filters (133)
packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_idle_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_idle_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playing_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playing_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playlist_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playlist_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_issue_2369.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/group_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/attachment_modal_sheet_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/edit_message_sheet_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/error_alert_sheet_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/channel/goldens/ci/channel_header_bottom_widget.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/context_menu_items/goldens/ci/download_menu_item_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/context_menu_items/goldens/ci/stream_chat_context_menu_item_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/dialogs/goldens/ci/confirmation_dialog_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/dialogs/goldens/ci/delete_message_dialog_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/gallery/goldens/ci/gallery_footer_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/gallery/goldens/ci/gallery_header_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/icons/goldens/ci/stream_svg_icon_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/icons/goldens/ci/stream_svg_icon_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_idle_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_idle_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_hold_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_hold_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_locked_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_locked_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_stopped_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_stopped_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/goldens/ci/attachment_button_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/goldens/ci/clear_input_item_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/goldens/ci/command_button_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/goldens/ci/countdown_button_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_custom.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/message_widget/goldens/ci/message_text.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_custom_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_custom_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_empty_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_empty_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_inverted_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_inverted_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_less_data_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_less_data_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_progress_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_progress_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_timestamp_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/stream_timestamp_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/system_message_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/misc/goldens/ci/system_message_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_error_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_error_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_error_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_error_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_full_screen_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_full_screen_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_error.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_error.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_full_screen_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_full_screen_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_with_initial_value_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_with_initial_value_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_end_vote_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_end_vote_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_long_question_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_long_question_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_all_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_all_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_disabled_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_disabled_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_limited_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_limited_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_unique_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_unique_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_with_initial_option_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_with_initial_option_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/draft_scroll_view/goldens/ci/stream_draft_list_tile_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/draft_scroll_view/goldens/ci/stream_draft_list_tile_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_unread_threads_banner_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_unread_threads_banner_light.pngis excluded by!**/*.png
📒 Files selected for processing (2)
packages/stream_chat_flutter/CHANGELOG.md(1 hunks)packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.dart(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.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). (9)
- GitHub Check: build (android)
- GitHub Check: test
- GitHub Check: analyze
- GitHub Check: update_goldens
- GitHub Check: stream_chat_flutter
- GitHub Check: stream_chat_localizations
- GitHub Check: stream_chat_persistence
- GitHub Check: stream_chat_flutter_core
- GitHub Check: analyze_legacy_versions
There was a problem hiding this 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 (2)
packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.dart (2)
294-309: Nit:mainAxisAlignmentis redundant withExpandedchildren.With all children wrapped in
Expanded,MainAxisAlignment.spaceEvenlyhas no visible effect. Remove for clarity.return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: users.map((userData) {
364-371: Default brightness for deterministic goldens (optional).Setting a non-null default avoids accidental theme drift in screenshots.
-Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { +Widget _wrapWithMaterialApp( + Widget widget, { + Brightness brightness = Brightness.light, +}) {
📜 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.
📒 Files selected for processing (1)
packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.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). (6)
- GitHub Check: test
- GitHub Check: build (ios)
- GitHub Check: build (android)
- GitHub Check: analyze_legacy_versions
- GitHub Check: stream_chat_flutter
- GitHub Check: stream_chat_localizations
🔇 Additional comments (3)
packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.dart (3)
1-1: No action needed.File-level lint ignore is reasonable for test descriptions.
271-285: Dart 3 SDK constraint confirmed — no action required.packages/stream_chat_flutter/pubspec.yaml sets environment.sdk = ^3.6.2 (>=3.6.2 <4.0.0), so Dart 3 features like records are supported.
118-132: Golden present — verify it's up-to-date and CI fonts/theme are fixed
- Found golden: test/src/avatars/goldens/ci/gradient_avatar_issue_2369.png — ensure this image was generated from the current test output and updated if needed.
- Found test config: test/flutter_test_config.dart — confirm CI loads this config (locks fonts and theme) to avoid cross-platform golden flakiness.
There was a problem hiding this 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
🧹 Nitpick comments (1)
packages/stream_chat_flutter/test/src/channel/channel_header_test.dart (1)
13-18: Optional: switch to setUpAll/tearDownAll to reduce per-test churn.Functionally fine as-is. Using per-file setup keeps the override stable across tests and avoids repeated writes.
Apply:
- TestWidgetsFlutterBinding.ensureInitialized(); - - final originalPathProviderPlatform = PathProviderPlatform.instance; - setUp(() => PathProviderPlatform.instance = FakePathProviderPlatform()); - tearDown(() => PathProviderPlatform.instance = originalPathProviderPlatform); + TestWidgetsFlutterBinding.ensureInitialized(); + + late final PathProviderPlatform originalPathProviderPlatform; + setUpAll(() { + originalPathProviderPlatform = PathProviderPlatform.instance; + PathProviderPlatform.instance = FakePathProviderPlatform(); + }); + tearDownAll(() { + PathProviderPlatform.instance = originalPathProviderPlatform; + });
📜 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.
⛔ Files ignored due to path filters (18)
packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_issue_2369.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/group_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_light.pngis excluded by!**/*.png
📒 Files selected for processing (1)
packages/stream_chat_flutter/test/src/channel/channel_header_test.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). (6)
- GitHub Check: build (android)
- GitHub Check: build (ios)
- GitHub Check: test
- GitHub Check: analyze_legacy_versions
- GitHub Check: stream_chat_localizations
- GitHub Check: stream_chat_flutter
🔇 Additional comments (2)
packages/stream_chat_flutter/test/src/channel/channel_header_test.dart (2)
8-8: No action required — FakePathProviderPlatform mixes in MockPlatformInterfaceMixin.
Verified in packages/stream_chat_flutter/test/src/fakes.dart; token checks will pass.
5-5: Declare path_provider_platform_interface as a direct dev dependency.Importing a transitive package in tests can trigger the depend_on_referenced_packages lint; add path_provider_platform_interface to dev_dependencies in packages/stream_chat_flutter/pubspec.yaml (and in example/pubspec.yaml if the example tests import it).
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #2381 +/- ##
=======================================
Coverage 63.76% 63.77%
=======================================
Files 412 413 +1
Lines 25821 25814 -7
=======================================
- Hits 16466 16462 -4
+ Misses 9355 9352 -3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart
Outdated
Show resolved
Hide resolved
This commit introduces significant enhancements to the `StreamGradientAvatar` and its underlying rendering logic:
- **Jitter Implementation:** A new `Jitter` class has been added to introduce controlled randomness to the polygon grid in `StreamGradientAvatar`. This creates more visually diverse and dynamic avatars while maintaining deterministic output based on the user ID (seed). The `Jitter` class offers configurable intensity levels (none, light, medium, heavy, max) and allows for custom `Random` instances.
- **Refined Gradient Avatar:**
- `StreamGradientAvatar` now utilizes the `Jitter` class for its background polygon grid.
- The number of rows and columns in the polygon grid is now configurable (defaulting to 4x4).
- Initials display has been improved with responsive font sizing based on available space and the number of characters.
- The avatar uses a predefined list of color palettes for gradients, ensuring visually appealing combinations.
- The `PolygonGradientPainter` has been rewritten to be more efficient and customizable, incorporating the `Jitter` logic and handling cell painting.
- The old `Offset4` class and related logic have been removed in favor of the new `PolygonCell` and improved painter.
- **String Extension for Initials:** A new `initials` getter has been added to `StringX` to reliably extract up to two initials from a string.
- **Test Updates:**
- Golden tests for gradient avatars have been updated to reflect the new visual style.
- New tests for the `Jitter` class have been added to ensure its functionality.
- `GroupAvatar` tests have been updated to use a `FakeConnectivityPlatform` for better test isolation.
Overall, these changes result in more aesthetically pleasing and dynamic gradient avatars with improved performance and maintainability.
This commit addresses an issue where `GradientAvatar` would generate different avatars for the same `userId` due to the non-deterministic nature of `Random()`. The fix involves the following changes: - The `Random` instance is now seeded with `userId.hashCode` to ensure that the same user ID always produces the same sequence of random numbers. - `Jitter.custom` has been replaced with `Jitter(seed: seed)` to leverage the seeded `Random` instance for jitter generation. - The gradient selection now uses `seed.abs() % _palettes.length` to ensure a deterministic choice from the available palettes. These changes guarantee that `GradientAvatar` will consistently render the same avatar for a given `userId` across different runs and platforms.
There was a problem hiding this 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
♻️ Duplicate comments (2)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (2)
52-65: Avoid String.hashCode for seeding; use a stable hash and decouple palette choice from RNG state.
String.hashCodeis not stable across runs/platforms. Use a stable 32‑bit hash.- Picking the palette via the same RNG instance advances the stream before jittering, causing pattern shifts if reused.
- Pass a stable
seedinto the painter and re-seed a local RNG per paint.Apply this diff:
@@ @override Widget build(BuildContext context) { - final random = Random(userId.hashCode); - final gradient = _palettes[random.nextInt(_palettes.length)]; - final jitter = Jitter.custom(random: random, intensity: jitterIntensity); + final seed = _stableHash32(userId); + final gradient = _palettes[seed % _palettes.length]; + final jitter = Jitter(seed: seed, intensity: jitterIntensity); return RepaintBoundary( key: Key(userId), child: CustomPaint( painter: PolygonGradientPainter( - jitter: jitter, + seed: seed, + jitter: jitter, gradient: gradient, ), child: Center(child: _Initials(username: name)), ), ); } } + +// Stable FNV-1a 32-bit hash for deterministic seeding +int _stableHash32(String s) { + var hash = 0x811C9DC5; + for (final cu in s.codeUnits) { + hash ^= cu; + hash = (hash * 0x01000193) & 0xFFFFFFFF; + } + return hash & 0x7FFFFFFF; +}
139-180: Re-seed RNG per paint to keep visuals deterministic across repaints.Create a local
Random(seed)and derive jitter from it instead of consumingjitter's internal RNG.void paint(Canvas canvas, Size size) { if (size.isEmpty) return; @@ - // Build jittered grid points + // Build jittered grid points using a per-paint RNG + final rng = Random(seed); final points = List<Offset>.filled(cols1 * rows1, Offset.zero); @@ - if (isBorder) { - points[rowBase + c] = Offset(x, y); - } else { - points[rowBase + c] = jitter.applyTo(Offset(x, y), maxDx, maxDy); - } + if (isBorder) { + points[rowBase + c] = Offset(x, y); + } else { + final dx = (rng.nextDouble() * 2 - 1) * maxDx * jitter.intensity; + final dy = (rng.nextDouble() * 2 - 1) * maxDy * jitter.intensity; + points[rowBase + c] = Offset(x + dx, y + dy); + }
🧹 Nitpick comments (4)
packages/stream_chat_flutter/lib/src/utils/extensions.dart (1)
60-70: Initials: double-check characters import and grapheme handling.
- This getter relies on .characters. Ensure
package:characters/characters.dartis imported (directly or via a re-export), otherwise builds can fail depending on import paths.- Minor: For consistent UI sizing later, prefer using
initials.characters.length(not.length) when deciding font size in the avatar widget.packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (2)
82-101: Use grapheme count for font sizing and avoidwithValuesfor wider SDK compatibility.
.lengthcan miscount multi-codepoint initials; use.characters.length.withValuesmay require newer Flutter.withOpacity(0.7)is broadly compatible.- final fontSize = initials.length == 2 ? side / 3 : side / 2; + final fontSize = + initials.characters.length == 2 ? side / 3 : side / 2; @@ - // ignore: deprecated_member_use - color: Colors.white.withValues(alpha: 0.7), + color: Colors.white.withOpacity(0.7),
182-188: Account for seed changes in shouldRepaint.Add
seedto the comparison to ensure proper invalidation when the user changes.bool shouldRepaint(covariant PolygonGradientPainter old) => old.rows != rows || old.columns != columns || + old.seed != seed || old.jitter.intensity != jitter.intensity || !const ListEquality().equals(old.gradient, gradient);packages/stream_chat_flutter/test/src/fakes.dart (1)
146-158: LGTM: minimal fake for connectivity.The fake returns a single WIFI event; adequate for tests. Consider exposing a controller if future tests need to emit multiple changes.
📜 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.
⛔ Files ignored due to path filters (2)
packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_3.pngis excluded by!**/*.png
📒 Files selected for processing (7)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart(1 hunks)packages/stream_chat_flutter/lib/src/utils/extensions.dart(1 hunks)packages/stream_chat_flutter/lib/src/utils/jitter.dart(1 hunks)packages/stream_chat_flutter/lib/src/utils/utils.dart(1 hunks)packages/stream_chat_flutter/test/src/avatars/group_avatar_test.dart(1 hunks)packages/stream_chat_flutter/test/src/fakes.dart(2 hunks)packages/stream_chat_flutter/test/src/utils/jitter_test.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). (9)
- GitHub Check: test
- GitHub Check: build (ios)
- GitHub Check: build (android)
- GitHub Check: analyze
- GitHub Check: analyze_legacy_versions
- GitHub Check: stream_chat_flutter
- GitHub Check: stream_chat_localizations
- GitHub Check: stream_chat_flutter_core
- GitHub Check: stream_chat_persistence
🔇 Additional comments (3)
packages/stream_chat_flutter/lib/src/utils/utils.dart (1)
4-4: Good addition to the utils barrel.Re-exporting
jitter.dartmakes the API cohesive and simplifies imports.packages/stream_chat_flutter/test/src/utils/jitter_test.dart (1)
6-193: Solid coverage and determinism checks.Tests cover clamping, presets, determinism with seeds, and bounds—nice.
packages/stream_chat_flutter/test/src/avatars/group_avatar_test.dart (1)
13-21: LGTM: simpler, deterministic test setup.Using real
Member/Userobjects and a fake connectivity platform improves stability and removes flaky platform channel handling.
There was a problem hiding this 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
♻️ Duplicate comments (1)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (1)
52-55: Avoid seeding with String.hashCode; use a stable 32‑bit hash for determinism across platforms/runs.
String.hashCodeisn’t guaranteed stable between executions or platforms. This can break cross‑platform consistency and golden tests. Seed both the palette selection and jitter with a stable hash.Apply this diff:
- final hash = userId.hashCode; - final gradient = _palettes[hash.abs() % _palettes.length]; - final jitter = Jitter(seed: hash, intensity: jitterIntensity); + final stableSeed = _stableHash32(userId); + final gradient = _palettes[stableSeed % _palettes.length]; + final jitter = Jitter(seed: stableSeed, intensity: jitterIntensity);Add this helper (place near the top of the file):
// Stable FNV-1a 32-bit hash for seeding PRNG deterministically. int _stableHash32(String s) { var hash = 0x811C9DC5; // offset basis for (final cu in s.codeUnits) { hash ^= cu; hash = (hash * 0x01000193) & 0xFFFFFFFF; // FNV prime } return hash & 0x7FFFFFFF; // non-negative (Random seed requirement) }Is Dart's String.hashCode guaranteed to be stable across different executions and platforms?
🧹 Nitpick comments (5)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (5)
31-36: Enforce the documented jitterIntensity range with an assert.Prevents accidental out‑of‑range values.
- const StreamGradientAvatar({ + const StreamGradientAvatar({ super.key, required this.name, required this.userId, this.jitterIntensity = 0.4, - }); + }) : assert(jitterIntensity >= 0.0 && jitterIntensity <= 1.0);
58-58: Prefer ValueKey for clarity.Explicit
ValueKey<String>reads clearer and avoids dynamic Key factories.- key: Key(userId), + key: ValueKey<String>(userId),
96-98: Use withOpacity for wider SDK compatibility; drop the deprecated ignore.
withOpacity(0.7)is broadly supported; the lint suppression is unnecessary.- // ignore: deprecated_member_use - color: Colors.white.withValues(alpha: 0.7), + color: Colors.white.withOpacity(0.7),
119-125: Assert nonzero rows/columns and valid gradient length.Protects against division by zero and shader errors if misused.
- PolygonGradientPainter({ + PolygonGradientPainter({ this.rows = 5, this.columns = 5, required this.jitter, required this.gradient, - }); + }) : assert(rows > 0), + assert(columns > 0), + assert(gradient.length >= 2);
150-166: Minor perf: reuse a Path instance in the loop (optional).Allocation is small here (default 5×5), but reusing a
Pathavoids churn.- // Build cells from jittered points and draw them + // Build cells from jittered points and draw them + final reusablePath = Path(); for (var r = 0; r < rows; r++) { @@ - PolygonCell(a, b, e, d).paint(canvas, gradient); + PolygonCell(a, b, e, d).paint(canvas, gradient, reusablePath: reusablePath);And update
PolygonCell.paint:- void paint( - Canvas canvas, - List<Color> gradient, - ) { + void paint( + Canvas canvas, + List<Color> gradient, { + Path? reusablePath, + }) { final shader = ui.Gradient.linear(pointA, pointC, gradient); final paint = Paint()..shader = shader; - - final path = Path() + final path = (reusablePath?..reset()) ?? Path() ..moveTo(pointA.dx, pointA.dy)Also applies to: 168-178
📜 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.
⛔ Files ignored due to path filters (20)
packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_3.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_issue_2369.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/group_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_light.pngis excluded by!**/*.png
📒 Files selected for processing (1)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.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). (3)
- GitHub Check: test
- GitHub Check: build (android)
- GitHub Check: build (ios)
There was a problem hiding this 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
♻️ Duplicate comments (1)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (1)
52-55: Avoid seeding with String.hashCode; not stable across runs/platforms
String.hashCodeis not guaranteed to be stable between executions or platforms, which can break determinism and golden stability. Use a stable 32‑bit hash for both palette selection and jitter seeding.Apply:
@@ - final hash = userId.hashCode; - final gradient = _palettes[hash.abs() % _palettes.length]; - final jitter = Jitter(seed: hash, intensity: jitterIntensity); + final hash = _stableHash32(userId); + final gradient = _palettes[hash % _palettes.length]; + final jitter = Jitter(seed: hash, intensity: jitterIntensity);Add a stable hash helper after imports:
@@ import 'package:stream_chat_flutter/src/utils/utils.dart'; +// Stable FNV-1a 32-bit hash for deterministic seeding across platforms/runs. +int _stableHash32(String s) { + var h = 0x811C9DC5; // offset basis + for (final cu in s.codeUnits) { + h ^= cu; + h = (h * 0x01000193) & 0xFFFFFFFF; // FNV prime + } + return h & 0x7FFFFFFF; // non-negative +}
🧹 Nitpick comments (5)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (5)
56-58: Prefer ValueKey over Key(String)Use
ValueKey<String>for clarity and to match Flutter conventions.- return RepaintBoundary( - key: Key(userId), + return RepaintBoundary( + key: ValueKey(userId),
31-36: Enforce documented bounds for jitterIntensityThe docs say 0.0–1.0; add an assert to catch misuse early.
const StreamGradientAvatar({ super.key, required this.name, required this.userId, this.jitterIntensity = 0.4, - }); + }) : assert(jitterIntensity >= 0.0 && jitterIntensity <= 1.0);
83-101: Use withOpacity for broader SDK compatibility; clamp font size to a sane minimum
Color.withValuesrequires recent Flutter;withOpacityis universally supported. Also guard against tiny layouts producing unreadable text.final side = min(constraints.maxWidth, constraints.maxHeight); - final fontSize = initials.length == 2 ? side / 3 : side / 2; + final fontSize = initials.length == 2 ? side / 3 : side / 2; + final effectiveFontSize = max(8.0, fontSize); @@ style: TextStyle( - fontSize: fontSize, + fontSize: effectiveFontSize, fontWeight: FontWeight.w500, - // ignore: deprecated_member_use - color: Colors.white.withValues(alpha: 0.7), + color: Colors.white.withOpacity(0.7), ),If the project’s minimum Flutter SDK guarantees
Color.withValues, keep it and ignore this change. Please confirm your min SDK.
119-125: Constructor should validate painter invariantsRows/columns must be positive; gradients should have at least 2 colors.
PolygonGradientPainter({ this.rows = 5, this.columns = 5, required this.jitter, required this.gradient, - }); + }) : assert(rows > 0), + assert(columns > 0), + assert(gradient.length >= 2);
240-257: Optional: verify text contrast over lighter palettesSome palettes are pastel; with 70% white text, contrast may be marginal on small avatars. Consider full white (1.0) or dynamic foreground based on luminance if accessibility is a goal.
📜 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.
⛔ Files ignored due to path filters (20)
packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_3.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_issue_2369.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/group_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_light.pngis excluded by!**/*.png
📒 Files selected for processing (1)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.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). (1)
- GitHub Check: build (android)
There was a problem hiding this 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
♻️ Duplicate comments (2)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (2)
142-150: LGTM: Re-seeding Jitter per paint avoids RNG drift.Constructing Jitter inside paint() with a seed ensures frame-to-frame determinism.
52-62: Avoid String.hashCode for seeding; use a stable 32-bit hash.hashCode isn’t stable across runs/platforms and can break determinism/goldens. Seed and palette index should come from a stable hash.
Apply this diff within the shown lines:
- final jitterSeed = userId.hashCode; - final gradient = _palettes[jitterSeed.abs() % _palettes.length]; + final stableSeed = _stableHash32(userId); + final gradient = _palettes[stableSeed % _palettes.length]; @@ - painter: PolygonGradientPainter( + painter: PolygonGradientPainter( gradient: gradient, - jitterSeed: jitterSeed, + jitterSeed: stableSeed, jitterIntensity: jitterIntensity, ),And add this helper (top-level, near imports):
// Stable FNV-1a 32-bit hash for deterministic seeding (non-negative). int _stableHash32(String s) { var hash = 0x811C9DC5; for (final cu in s.codeUnits) { hash ^= cu; hash = (hash * 0x01000193) & 0xFFFFFFFF; } return hash & 0x7FFFFFFF; }
🧹 Nitpick comments (5)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (5)
55-56: Keying the RepaintBoundary by userId is optional.Unless you rely on keyed element identity, this key can be dropped (or use ValueKey(userId)) to avoid unnecessary element churn.
119-125: Harden constructor with asserts (rows/cols > 0, gradient >= 2, intensity in [0,1]).Prevents silent misuse and aids debug.
PolygonGradientPainter({ this.rows = 5, this.columns = 5, this.jitterSeed, this.jitterIntensity = 0.4, required this.gradient, - }); + }) : assert(rows > 0), + assert(columns > 0), + assert(jitterIntensity >= 0 && jitterIntensity <= 1), + assert(gradient.length >= 2);
93-99: Remove unnecessary deprecated_member_use ignore.withValues isn’t deprecated; drop the ignore (or use withOpacity if desired).
- // ignore: deprecated_member_use color: Colors.white.withValues(alpha: 0.7),
176-188: Tiny allocation nit: reuse Paint/Path inside loops.Hoist Paint/Path per frame and reuse; set shader per cell and reset the path.
- for (var r = 0; r < rows; r++) { + final paint = Paint(); + final path = Path(); + for (var r = 0; r < rows; r++) { @@ - PolygonCell(a, b, e, d).paint(canvas, gradient); + // Inline painting to reuse paint/path. + paint.shader = ui.Gradient.linear(a, e, gradient); + path + ..reset() + ..moveTo(a.dx, a.dy) + ..lineTo(b.dx, b.dy) + ..lineTo(e.dx, e.dy) + ..lineTo(d.dx, d.dy) + ..close(); + canvas.drawPath(path, paint);Also applies to: 228-247
8-16: Docs claim deterministic across sessions/platforms—ensure after stable hash change.Once you switch to _stableHash32, the docs will be accurate; otherwise this promise may not hold across VMs/architectures.
📜 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.
📒 Files selected for processing (1)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.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). (10)
- GitHub Check: analyze_legacy_versions
- GitHub Check: build (android)
- GitHub Check: build (ios)
- GitHub Check: test
- GitHub Check: stream_chat_persistence
- GitHub Check: stream_chat_flutter
- GitHub Check: stream_chat
- GitHub Check: analyze
- GitHub Check: stream_chat_localizations
- GitHub Check: stream_chat_flutter_core
🔇 Additional comments (1)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (1)
191-197: LGTM: shouldRepaint covers seed/intensity/shape/gradient.Including jitterSeed and gradient equality is correct for deterministic repaints.
There was a problem hiding this 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
♻️ Duplicate comments (1)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (1)
52-54: Avoid String.hashCode for deterministic seeding; use a stable 32‑bit hash.
String.hashCodeisn’t stable across runs/platforms, which breaks the “deterministic across sessions” guarantee and can flake goldens. Seed and palette index should derive from a stable hash ofuserId.Apply this diff:
- final jitterSeed = userId.hashCode; - final gradient = _palettes[jitterSeed.abs() % _palettes.length]; + final jitterSeed = _stableHash32(userId); + final gradient = _palettes[jitterSeed % _palettes.length];Add this helper in this file (library‑private):
// Stable FNV-1a 32-bit hash; returns non-negative seed. int _stableHash32(String s) { var hash = 0x811C9DC5; for (final cu in s.codeUnits) { hash ^= cu; hash = (hash * 0x01000193) & 0xFFFFFFFF; } return hash & 0x7FFFFFFF; }
🧹 Nitpick comments (3)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (3)
31-36: Enforce documented jitter range at the widget boundary.Doc says 0.0–1.0; add an assert to fail fast on invalid values.
Apply this diff:
const StreamGradientAvatar({ super.key, required this.name, required this.userId, this.jitterIntensity = 0.4, - }); + }) : assert(jitterIntensity >= 0 && jitterIntensity <= 1);
118-124: Harden painter constructor with basic invariants.Avoid silent bad inputs (e.g., zero rows/cols, <2 gradient colors).
Apply this diff:
PolygonGradientPainter({ this.rows = 5, this.columns = 5, this.jitterSeed, this.jitterIntensity = 0.4, required this.gradient, - }); + }) : assert(rows > 0), + assert(columns > 0), + assert(jitterIntensity >= 0 && jitterIntensity <= 1), + assert(gradient.length >= 2);
90-98: Remove unnecessary lint suppression.
TextStyle.colorandColors.white.withOpacityaren’t deprecated; the// ignore: deprecated_member_usecan be dropped.Apply this diff:
fontWeight: FontWeight.w500, - // ignore: deprecated_member_use color: Colors.white.withOpacity(0.7),
📜 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.
⛔ Files ignored due to path filters (8)
packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_1.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_3.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_issue_2369.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/group_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_1.pngis excluded by!**/*.png
📒 Files selected for processing (1)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.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). (10)
- GitHub Check: update_goldens
- GitHub Check: test
- GitHub Check: build (android)
- GitHub Check: analyze
- GitHub Check: stream_chat_flutter_core
- GitHub Check: stream_chat_flutter
- GitHub Check: stream_chat_localizations
- GitHub Check: stream_chat_persistence
- GitHub Check: stream_chat
- GitHub Check: analyze_legacy_versions
🔇 Additional comments (3)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (3)
55-63: Good: deterministic painter wiring and repaint boundary.Passing the seed/intensity into the painter and wrapping with RepaintBoundary is solid.
145-149: Good: re-instantiating Jitter in paint avoids RNG drift across frames.This keeps geometry stable per seed.
150-157: Verify jitter displacement bounds to prevent cell inversions at high intensity.With
maxDx = cellWandmaxDy = cellH, interior points could cross edges ifJitter.applyTouses full ranges, producing self‑intersecting quads. If not already bounded insideJitter, consider limiting to a fraction (e.g., 0.3–0.5 of cell size) or clamping to keep A–B–E–D ordering intact.Would you confirm
Jitter.applyToguarantees displacements that keep polygons non-inverted forjitterIntensityin [0,1]? If not, I can propose a clamp.Also applies to: 170-171, 175-186
There was a problem hiding this 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
♻️ Duplicate comments (1)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (1)
52-61: Don’t seed visuals with String.hashCode; use a stable, non‑negative 32‑bit hash.hashCode isn’t guaranteed stable across runs/platforms and may be negative. This can cause cross‑device/golden flakiness. Derive a stable seed and normalize it before palette selection and jitter.
Apply:
- final jitterSeed = userId.hashCode; - final gradient = _palettes[jitterSeed.abs() % _palettes.length]; + final seed = _stableHash32(userId); // stable, non-negative + final gradient = _palettes[seed % _palettes.length]; @@ - painter: PolygonGradientPainter( - gradient: gradient, - jitterSeed: jitterSeed, - jitterIntensity: jitterIntensity, - ), + painter: PolygonGradientPainter( + gradient: gradient, + jitterSeed: seed, + jitterIntensity: jitterIntensity, + ),Add helper (top-level, outside classes):
// Stable FNV-1a 32-bit hash suitable for seeding PRNGs. int _stableHash32(String s) { var h = 0x811C9DC5; for (final cu in s.codeUnits) { h ^= cu; h = (h * 0x01000193) & 0xFFFFFFFF; } return h & 0x7FFFFFFF; // non-negative }
🧹 Nitpick comments (3)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (3)
31-36: Validate jitterIntensity at construction time.Add a constructor assert to prevent out‑of‑range values.
- const StreamGradientAvatar({ + const StreamGradientAvatar({ super.key, required this.name, required this.userId, this.jitterIntensity = 0.4, - }); + }) : assert(jitterIntensity >= 0.0 && jitterIntensity <= 1.0);
120-126: Harden painter invariants with asserts.Guard against invalid rows/columns, gradient length, and jitter range.
PolygonGradientPainter({ this.rows = 5, this.columns = 5, this.jitterSeed, this.jitterIntensity = 0.4, required this.gradient, - }); + }) : assert(rows > 0), + assert(columns > 0), + assert(jitterIntensity >= 0.0 && jitterIntensity <= 1.0), + assert(gradient.length >= 2);
96-98: Remove stale deprecation ignore.Colors.white.withOpacity isn’t deprecated; the ignore adds noise.
- // ignore: deprecated_member_use color: Colors.white.withOpacity(0.7),
📜 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.
⛔ Files ignored due to path filters (16)
packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_0.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_2.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_3.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_issue_2369.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_light.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_dark.pngis excluded by!**/*.pngpackages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_light.pngis excluded by!**/*.png
📒 Files selected for processing (2)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart(1 hunks)packages/stream_chat_flutter/lib/src/dialogs/channel_info_dialog.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). (10)
- GitHub Check: stream_chat
- GitHub Check: stream_chat_localizations
- GitHub Check: stream_chat_flutter_core
- GitHub Check: stream_chat_flutter
- GitHub Check: stream_chat_persistence
- GitHub Check: build (android)
- GitHub Check: test
- GitHub Check: build (ios)
- GitHub Check: analyze
- GitHub Check: analyze_legacy_versions
🔇 Additional comments (3)
packages/stream_chat_flutter/lib/src/dialogs/channel_info_dialog.dart (1)
58-61: Tight constraints may clip in narrow layouts; confirm this is safe across dialog breakpoints.Switching to BoxConstraints.tightFor(64×64) forces an exact size. If parent constraints are smaller (e.g., small dialogs, large text scale), this can overflow/clamp. If that’s intended for visual consistency, LGTM—otherwise consider keeping max constraints or wrapping in a SizedBox with fit behavior.
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (2)
147-151: Good: re‑seeding Jitter per paint avoids RNG drift.Creating Jitter inside paint() with a seed keeps frames deterministic for the same input. Nice.
192-199: Good: shouldRepaint covers all determinants.Comparing rows, columns, jitterSeed, jitterIntensity, and gradient via ListEquality is correct and avoids stale frames.
| // ignore: deprecated_member_use | ||
| color: Colors.white.withOpacity(0.7), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like ignoring this warning, as we should move away from it at some point and this completely removes the warning from our view.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this 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
🧹 Nitpick comments (2)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (2)
93-98: Prefer Color.withOpacity for broader Flutter compatibilitywithValues may not be available on older Flutter channels. Use withOpacity(0.7).
- color: Colors.white.withValues(alpha: 0.7), + color: Colors.white.withOpacity(0.7),If you intentionally target a Flutter version that supports withValues, ensure the SDK constraint reflects that.
119-125: Add constructor asserts to enforce documented invariantsGuarantee valid grid/jitter configuration at construction time.
PolygonGradientPainter({ this.rows = 5, this.columns = 5, this.jitterSeed, this.jitterIntensity = 0.4, required this.gradient, - }); + }) : assert(rows > 0), + assert(columns > 0), + assert(gradient.length >= 2), + assert(jitterIntensity >= 0.0 && jitterIntensity <= 1.0);
📜 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.
📒 Files selected for processing (1)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.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). (10)
- GitHub Check: stream_chat_flutter
- GitHub Check: build (android)
- GitHub Check: analyze
- GitHub Check: stream_chat_localizations
- GitHub Check: stream_chat_flutter_core
- GitHub Check: build (ios)
- GitHub Check: stream_chat
- GitHub Check: stream_chat_persistence
- GitHub Check: test
- GitHub Check: analyze_legacy_versions
🔇 Additional comments (3)
packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart (3)
146-150: Good: re-instantiating Jitter inside paint avoids RNG drift across frames
191-197: Good: shouldRepaint compares seed/intensity/gradient to prevent stale frames
52-60: Replace String.hashCode with a stable hash for deterministic visualsString.hashCode isn’t stable across runs/platforms; seeds and palettes may change between sessions/devices. Use a stable 32‑bit hash.
Apply this diff:
- final jitterSeed = userId.hashCode; - final gradient = _palettes[jitterSeed.abs() % _palettes.length]; + final stableSeed = _stableHash32(userId); + final gradient = _palettes[stableSeed % _palettes.length]; @@ - painter: PolygonGradientPainter( + painter: PolygonGradientPainter( gradient: gradient, - jitterSeed: jitterSeed, + jitterSeed: stableSeed, jitterIntensity: jitterIntensity, ),Add this helper near the top of the file (outside any class):
// Stable FNV-1a 32-bit hash for deterministic seeding (non-negative). int _stableHash32(String s) { var hash = 0x811C9DC5; for (final cu in s.codeUnits) { hash ^= cu; hash = (hash * 0x01000193) & 0xFFFFFFFF; } return hash & 0x7FFFFFFF; }
Fixes: #2369
Submit a pull request
This PR fixes an issue where GradientAvatars for users with same-length IDs would have identical colors.
The
GradientAvatarPainternow usesRandom(userId.hashCode)instead ofRandom(userId.length)to generate colors and transform points. This ensures that different user IDs, even if they have the same length, will produce visually distinct avatars.A regression test (
gradient_avatar_issue_2369) has been added to verify this fix, including the specific scenario reported in GitHub issue #2369.Additionally, the golden test theme in
flutter_test_config.darthas been updated with a new background color, border color, name text style, and padding for improved visual clarity in golden tests.Summary by CodeRabbit
New Features
Bug Fixes / Improvements
Tests
Documentation