Skip to content

Conversation

@xsahil03x
Copy link
Member

@xsahil03x xsahil03x commented Nov 18, 2025

Submit a pull request

Fixes: #2228

Description of the pull request

This PR refactors image thumbnail handling for better performance and efficiency.

It introduces ThumbnailSizeCalculator, a utility that calculates the optimal dimensions for thumbnails based on the widget's constraints and the device's pixel ratio. This ensures images are decoded and cached at an appropriate size, preventing excessive memory usage from large images.

Key changes:

  • StreamImageAttachmentThumbnail now uses LayoutBuilder to determine the available space and calculates the best thumbnail size. It also passes the calculated memCacheWidth and memCacheHeight to CachedNetworkImage for optimized caching.
  • UserAvatar and StreamChannelAvatar are updated to leverage this new mechanism, resizing avatar images to fit their display constraints, which improves performance, especially in lists.
  • GiphyAttachmentThumbnail and VideoAttachmentThumbnail are simplified to delegate to StreamImageAttachmentThumbnail, centralizing the image loading logic.
  • The direct usage of CachedNetworkImage in GalleryFooter is replaced with StreamImageAttachmentThumbnail for consistency.

Summary by CodeRabbit

  • Bug Fixes

    • Resolved high memory consumption when displaying multiple image attachments
    • Fixed hyperlink text parameter passing to onLinkTap callback
  • Improvements

    • Smarter thumbnail sizing and caching for images, videos, GIFs and avatars to improve loading and display
    • Unified thumbnail rendering with more reliable error/fallback behavior
  • Tests

    • Added tests for thumbnail size calculation
  • Documentation

    • Added changelog entry for memory fix

This commit refactors image thumbnail handling for better performance and efficiency.

It introduces `ThumbnailSizeCalculator`, a utility that calculates the optimal dimensions for thumbnails based on the widget's constraints and the device's pixel ratio. This ensures images are decoded and cached at an appropriate size, preventing excessive memory usage from large images.

Key changes:
- **`StreamImageAttachmentThumbnail`** now uses `LayoutBuilder` to determine the available space and calculates the best thumbnail size. It also passes the calculated `memCacheWidth` and `memCacheHeight` to `CachedNetworkImage` for optimized caching.
- **`UserAvatar`** and **`StreamChannelAvatar`** are updated to leverage this new mechanism, resizing avatar images to fit their display constraints, which improves performance, especially in lists.
- **`GiphyAttachmentThumbnail`** and **`VideoAttachmentThumbnail`** are simplified to delegate to `StreamImageAttachmentThumbnail`, centralizing the image loading logic.
- The direct usage of `CachedNetworkImage` in `GalleryFooter` is replaced with `StreamImageAttachmentThumbnail` for consistency.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 18, 2025

Walkthrough

Introduces dynamic thumbnail sizing, cache-dimension propagation, and thumbnail-rendering refactors across image, video, giphy, avatar, and gallery components; adds ThumbnailSizeCalculator and tests; updates changelog and makes imageThumbnailSize nullable in StreamImageAttachment.

Changes

Cohort / File(s) Summary
Changelog
packages/stream_chat_flutter/CHANGELOG.md
Added entry documenting a fix for high memory usage when displaying multiple image attachments.
Attachment API
packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart
Made imageThumbnailSize nullable and removed its default value; constructor and field type changed from Size to Size?.
Thumbnail utility
packages/stream_chat_flutter/lib/src/attachment/thumbnail/thumbnail_size_calculator.dart
Added ThumbnailSizeCalculator with static Size? calculate(...) to compute optimal thumbnail dimensions preserving aspect ratio and applying device pixel ratio.
Image thumbnail renderer
packages/stream_chat_flutter/lib/src/attachment/thumbnail/image_attachment_thumbnail.dart
Reworked rendering to use LayoutBuilder, compute effective thumbnail size via ThumbnailSizeCalculator, add cacheWidth/cacheHeight, support local vs remote image paths, and resize remote URLs when possible.
Giphy / Video thumbnail paths
packages/stream_chat_flutter/lib/src/attachment/thumbnail/giphy_attachment_thumbnail.dart, packages/stream_chat_flutter/lib/src/attachment/thumbnail/video_attachment_thumbnail.dart
Simplified to always use StreamImageAttachmentThumbnail, removed CachedNetworkImage specific placeholders/errorWidgets, and centralized error handling.
Gallery grid thumbnails
packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart
Replaced CachedNetworkImage usage for non-video items with StreamImageAttachmentThumbnail; removed cached_network_image import.
Avatars & channel avatars
packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart, packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart
Switched to LayoutBuilder-based cache dimension computation from constraints and devicePixelRatio; updated image loading to use computed cache sizes and ResizeImage/CachedNetworkImage imageBuilder styling.
Tests
packages/stream_chat_flutter/test/src/attachment/thumbnail/thumbnail_size_calculator_test.dart
Added comprehensive tests for ThumbnailSizeCalculator.calculate covering null/invalid inputs, finite/infinite constraints, aspect ratios, and pixel-ratio variations.

Sequence Diagram(s)

sequenceDiagram
    participant Widget as Attachment/Avatar Widget
    participant Layout as LayoutBuilder
    participant Calc as ThumbnailSizeCalculator
    participant Loader as StreamImageAttachmentThumbnail
    participant Cache as Image Cache / Engine

    Widget->>Layout: build → provide constraints
    Layout->>Calc: calculate(originalSize?, targetSize, pixelRatio)
    Calc-->>Layout: return effective thumbnail Size? (or null)
    Layout->>Loader: render with url/file + cacheWidth/cacheHeight
    Loader->>Cache: request image with memCacheWidth/memCacheHeight
    Cache-->>Loader: deliver optimized image bytes
    Loader-->>Widget: paint thumbnail

    rect rgb(220,240,220)
    Note over Calc,Cache: New: dynamic sizing and cache dims\nOld: fixed 400x400 → higher memory use
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing extra attention:
    • image_attachment_thumbnail.dart: LayoutBuilder branching, cache dimension propagation, and remote URL resizing.
    • user_avatar.dart & stream_channel_avatar.dart: constraint-derived sizing and visual fallback correctness.
    • Public API nullability change in image_attachment.dart: verify downstream call sites and ensure no runtime null issues.
    • ThumbnailSizeCalculator: numeric edge cases (infinite constraints, zero dimensions) and test correctness.

Suggested reviewers

  • renefloor
  • Brazol

Poem

🐰 I measured the bounds, then scaled with delight,

Thumbnails that fit, no more memory fright.
Cache bytes restrained, images light as a song,
Multiple pics? Now the app hums along.
Hop, nibble, optimize — and code bounces on! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: refactoring UI to optimize image thumbnail loading and caching through ThumbnailSizeCalculator and LayoutBuilder-based sizing.
Linked Issues check ✅ Passed The PR directly addresses issue #2228 by reducing memory usage through optimized thumbnail sizing and caching, preventing high-memory crashes on iOS when displaying multiple images.
Out of Scope Changes check ✅ Passed All changes are directly related to optimizing image thumbnail loading and caching to address the high memory usage issue; no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/attachment-gallery-high-memory

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

❤️ Share

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

Copy link
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

🧹 Nitpick comments (6)
packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart (1)

119-151: Guard cache dimensions against unbounded/zero constraints

The new LayoutBuilder correctly ties memCacheWidth/memCacheHeight to the rendered avatar size, but it assumes constraints.biggest is finite and non‑zero. In more flexible layouts (e.g. missing constraints/theme constraints, or unusual parents), this can yield 0 or infinite dimensions, which may trip assertions or cause unexpected behavior in CachedNetworkImage.

You can make this more robust by clamping to positive, finite values and skipping memCacheWidth/memCacheHeight when they’re not valid, e.g.:

final devicePixel = MediaQuery.devicePixelRatioOf(context);
final biggest = constraints.biggest;
final scaled = biggest * devicePixel;

int? cacheWidth;
int? cacheHeight;
if (scaled.width.isFinite && scaled.height.isFinite && 
    scaled.width > 0 && scaled.height > 0) {
  cacheWidth = scaled.width.round();
  cacheHeight = scaled.height.round();
}

return CachedNetworkImage(
  imageUrl: channelImage,
  memCacheWidth: cacheWidth,
  memCacheHeight: cacheHeight,
  errorWidget: (_, __, ___) => fallbackWidget,
  fit: BoxFit.cover,
);

Also, if channel.image/imageStream can ever be null at runtime, consider guarding channelImage.isEmpty accordingly.

packages/stream_chat_flutter/lib/src/attachment/thumbnail/video_attachment_thumbnail.dart (1)

3-4: Tighten thumbnail availability check and confirm StreamImageAttachmentThumbnail semantics

The new thumbUrl path is a nice simplification, and reusing StreamImageAttachmentThumbnail should help with consistent sizing/caching.

Two small follow‑ups:

  • Consider treating only non‑empty URLs as “has thumbnail” to avoid passing an empty string through:
final containsThumbnail = (video.thumbUrl ?? '').isNotEmpty;
if (containsThumbnail) {
  return StreamImageAttachmentThumbnail(
    image: video,
    width: width,
    height: height,
    fit: fit,
    errorBuilder: errorBuilder,
  );
}
  • Please double‑check that StreamImageAttachmentThumbnail correctly handles AttachmentType.video by using thumbUrl rather than imageUrl, so we don’t regress any cases where videos relied on their thumbnail.

Also applies to: 57-67

packages/stream_chat_flutter/lib/src/attachment/thumbnail/giphy_attachment_thumbnail.dart (1)

58-68: Giphy now correctly reuses the shared image thumbnail pipeline

Using giphy.giphyInfo(type) and delegating to StreamImageAttachmentThumbnail keeps sizing, caching, and error handling consistent with other attachments. This looks good and should simplify future maintenance. The only subtle dependency is that Attachment.copyWith(imageUrl: info?.url) relies on copyWith preserving the existing imageUrl when info?.url is null; if that ever changes, switching to image: giphy.copyWith(imageUrl: info?.url ?? giphy.imageUrl) would make the fallback explicit.

packages/stream_chat_flutter/test/src/attachment/thumbnail/thumbnail_size_calculator_test.dart (1)

1-272: ThumbnailSizeCalculator test coverage is strong

The tests exercise the critical paths (null/invalid inputs, finite vs infinite constraints, a variety of aspect ratios, and multiple pixel ratios) and closely mirror the intended algorithm. This should give good confidence that sizing and scaling behave as expected. If you want even more robustness, you could add a couple of edge tests for degenerate inputs (e.g., pixelRatio <= 0 or targetSize with a zero dimension) to lock in behavior there as well.

packages/stream_chat_flutter/lib/src/attachment/thumbnail/thumbnail_size_calculator.dart (1)

1-78: Aspect‑ratio–preserving size calculation looks correct; consider tightening a couple of edge cases

The core algorithm (handling infinities, preserving aspect ratio, and then applying pixelRatio) is sound and aligns with the tests. A couple of small refinements could make it more robust:

  • Treat obviously invalid pixelRatio values (e.g., <= 0) as a signal to skip optimization and return null.
  • After computing thumbnailWidth/thumbnailHeight, bail out if either ends up <= 0 instead of returning a zero‑sized Size, which would later yield cacheWidth/cacheHeight of 0.

For example:

   static Size? calculate({
     Size? originalSize,
     required Size targetSize,
     required double pixelRatio,
   }) {
+    if (pixelRatio <= 0) return null;
@@
-    // Calculate size that maintains aspect ratio within constraints
-    final targetAspectRatio = thumbnailWidth / thumbnailHeight;
+    // Calculate size that maintains aspect ratio within constraints
+    final targetAspectRatio = thumbnailWidth / thumbnailHeight;
@@
-    // Apply pixel ratio to get physical pixel dimensions
-    return Size(thumbnailWidth * pixelRatio, thumbnailHeight * pixelRatio);
+    if (thumbnailWidth <= 0 || thumbnailHeight <= 0) {
+      return null;
+    }
+
+    // Apply pixel ratio to get physical pixel dimensions
+    return Size(thumbnailWidth * pixelRatio, thumbnailHeight * pixelRatio);
   }

This keeps behavior unchanged for normal cases but avoids propagating obviously unusable sizes.

packages/stream_chat_flutter/lib/src/attachment/thumbnail/image_attachment_thumbnail.dart (1)

75-135: Layout‑based sizing + ThumbnailSizeCalculator integration achieves the intended memory optimization

The LayoutBuilder + ThumbnailSizeCalculator flow and the unified remote/local handling look well thought through:

  • effectiveThumbnailSize is computed once per build, with thumbnailSize overriding the calculated size, and falls back cleanly when originalSize or constraints don’t allow optimization.
  • Using image.thumbUrl ?? image.imageUrl prioritizes server‑side thumbnails while still handling plain image URLs.
  • Passing cacheWidth/cacheHeight derived from effectiveThumbnailSize into both remote and local paths ensures decode sizes are aligned with layout, which should significantly reduce memory pressure in galleries.

Two minor, defensive tweaks you might consider:

  • If MediaQuery.devicePixelRatioOf(context) isn’t available (e.g., in certain test setups or if you ever support older Flutter versions), defaulting to 1.0 would make this more resilient.
  • If effectiveThumbnailSize ever ends up with non‑positive dimensions (e.g., due to odd constraints), it may be safer to treat that the same as null and skip setting cacheWidth/cacheHeight and URL resizing rather than passing zeros through.

Overall, this is a solid step toward predictable thumbnail sizing and caching.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3b5e410 and 9215fc1.

📒 Files selected for processing (10)
  • packages/stream_chat_flutter/CHANGELOG.md (1 hunks)
  • packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart (2 hunks)
  • packages/stream_chat_flutter/lib/src/attachment/thumbnail/giphy_attachment_thumbnail.dart (1 hunks)
  • packages/stream_chat_flutter/lib/src/attachment/thumbnail/image_attachment_thumbnail.dart (7 hunks)
  • packages/stream_chat_flutter/lib/src/attachment/thumbnail/thumbnail_size_calculator.dart (1 hunks)
  • packages/stream_chat_flutter/lib/src/attachment/thumbnail/video_attachment_thumbnail.dart (2 hunks)
  • packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart (3 hunks)
  • packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart (1 hunks)
  • packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart (1 hunks)
  • packages/stream_chat_flutter/test/src/attachment/thumbnail/thumbnail_size_calculator_test.dart (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-25T08:19:01.469Z
Learnt from: xsahil03x
Repo: GetStream/stream-chat-flutter PR: 2394
File: packages/stream_chat_flutter/lib/src/message_action/message_actions_builder.dart:82-92
Timestamp: 2025-09-25T08:19:01.469Z
Learning: In the Stream Chat Flutter library, when deleting a message with MessageSendingStatus.failed or MessageSendingStatus.failed_update status, the _deleteMessage method in channel.dart automatically handles deletion locally via _deleteLocalMessage without making API calls, preventing 404 errors and deletingFailed states.

Applied to files:

  • packages/stream_chat_flutter/lib/src/attachment/thumbnail/image_attachment_thumbnail.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: analyze_legacy_versions
  • GitHub Check: build (android)
  • GitHub Check: build (ios)
  • GitHub Check: test
  • GitHub Check: stream_chat_localizations
  • GitHub Check: stream_chat_flutter_core
  • GitHub Check: stream_chat_persistence
  • GitHub Check: stream_chat_flutter
🔇 Additional comments (5)
packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart (1)

240-249: Centralizing grid thumbnails on StreamImageAttachmentThumbnail looks good

Switching the grid item from a raw CachedNetworkImage to StreamImageAttachmentThumbnail keeps the existing layout/gesture behavior while reusing the new thumbnail sizing/caching logic. This is a good alignment with the rest of the refactor.

packages/stream_chat_flutter/CHANGELOG.md (1)

16-18: Changelog entry clearly documents the memory fix

The new bullet explicitly ties the high‑memory fix to image attachments and links back to #2228, which nicely documents the behavior change for consumers.

packages/stream_chat_flutter/lib/src/attachment/thumbnail/image_attachment_thumbnail.dart (2)

138-191: Local image attachments now respect cache dimensions consistently

Threading cacheWidth/cacheHeight into _LocalImageAttachment and forwarding them to both Image.memory and Image.file aligns local image decoding with the same sizing logic used for remote images. That should avoid decoding full‑resolution local photos when they’re shown as thumbnails and materially reduce per‑image memory usage without changing layout behavior.


194-245: Remote image attachments correctly use memCacheWidth/memCacheHeight

Propagating cacheWidth/cacheHeight into CachedNetworkImage’s memCacheWidth/memCacheHeight, and optionally resizing the URL via getResizedImageUrl when an effective thumbnail size is available, neatly aligns server‑side resizing with client‑side decode constraints. This should directly address the high‑memory gallery scenarios while keeping placeholders and error handling unchanged.

packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart (1)

15-35: Making imageThumbnailSize nullable aligns this API with the new sizing strategy

Removing the hard‑coded const Size(400, 400) default and making imageThumbnailSize nullable so that it simply passes through to StreamImageAttachmentThumbnail.thumbnailSize is consistent with the new layout‑driven sizing. Callers that never customized imageThumbnailSize will now get automatically computed sizes instead of a fixed 400×400 cache target, which is desirable for memory. It’s worth double‑checking any internal usages that relied on that specific default, but from an API perspective this change looks coherent.

@codecov
Copy link

codecov bot commented Nov 18, 2025

Codecov Report

❌ Patch coverage is 66.31579% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.60%. Comparing base (3b5e410) to head (08fee37).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...tachment/thumbnail/image_attachment_thumbnail.dart 52.94% 16 Missing ⚠️
...ream_chat_flutter/lib/src/avatars/user_avatar.dart 66.66% 9 Missing ⚠️
...tachment/thumbnail/video_attachment_thumbnail.dart 0.00% 4 Missing ⚠️
...ttachment/thumbnail/thumbnail_size_calculator.dart 93.75% 1 Missing ⚠️
...flutter/lib/src/channel/stream_channel_avatar.dart 90.00% 1 Missing ⚠️
...m_chat_flutter/lib/src/gallery/gallery_footer.dart 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2444      +/-   ##
==========================================
+ Coverage   64.53%   64.60%   +0.07%     
==========================================
  Files         419      420       +1     
  Lines       26196    26210      +14     
==========================================
+ Hits        16905    16934      +29     
+ Misses       9291     9276      -15     

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

This commit adds a check to ensure `thumbnailSize` is finite and not empty before calculating `cacheWidth` and `cacheHeight` for `UserAvatar` and `StreamChannelAvatar`.

Previously, if the avatar widget was rendered with unconstrained dimensions, `thumbnailSize` could be infinite, leading to a crash when `round()` was called. This change prevents the crash by only setting `cacheWidth` and `cacheHeight` for `CachedNetworkImage` if the constraints are finite.
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: 0

♻️ Duplicate comments (1)
packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart (1)

80-141: Avatar thumbnail still double‑decodes and doesn’t use cache dimensions for CachedNetworkImage

Lines 108–116 compute cacheWidth/cacheHeight but they’re only used inside ResizeImage in imageBuilder, not passed to CachedNetworkImage itself. Combined with CachedNetworkImage’s own decoding, this likely causes:

  • A full‑resolution decode for the cached image.
  • A second decode at avatar size via ResizeImage.

That works functionally but undercuts the PR’s memory‑reduction goal and can keep both large and resized bitmaps in memory.

You can simplify the pipeline and align it with the thumbnail strategy used elsewhere in the PR by:

  1. Guarding the computed size per dimension (finite and > 0).
  2. Passing those dimensions directly into CachedNetworkImage via memCacheWidth/memCacheHeight.
  3. Removing ResizeImage so the cached, correctly‑sized image is used directly.
  4. Ensuring effectiveBorderRadius is always non‑null where needed.

Example patch:

-            // Calculate optimal thumbnail size for the avatar
-            final devicePixelRatio = MediaQuery.devicePixelRatioOf(context);
-            final thumbnailSize = constraints.biggest * devicePixelRatio;
-
-            int? cacheWidth, cacheHeight;
-            if (thumbnailSize.isFinite && !thumbnailSize.isEmpty) {
-              cacheWidth = thumbnailSize.width.round();
-              cacheHeight = thumbnailSize.height.round();
-            }
+            // Calculate optimal thumbnail size for the avatar.
+            final devicePixelRatio = MediaQuery.devicePixelRatioOf(context);
+            final thumbnailSize = constraints.biggest * devicePixelRatio;
+
+            int? cacheWidth, cacheHeight;
+            if (thumbnailSize.width.isFinite &&
+                thumbnailSize.height.isFinite &&
+                thumbnailSize.width > 0 &&
+                thumbnailSize.height > 0) {
+              cacheWidth = thumbnailSize.width.round();
+              cacheHeight = thumbnailSize.height.round();
+            }
@@
-            return CachedNetworkImage(
-              fit: BoxFit.cover,
-              filterQuality: FilterQuality.high,
-              imageUrl: imageUrl,
-              errorWidget: (_, __, ___) => backupGradientAvatar,
-              placeholder: switch (effectivePlaceholder) {
-                final holder? => (context, __) => holder(context, user),
-                _ => null,
-              },
-              imageBuilder: (context, imageProvider) => DecoratedBox(
-                decoration: BoxDecoration(
-                  borderRadius: effectiveBorderRadius,
-                  image: DecorationImage(
-                    fit: BoxFit.cover,
-                    image: ResizeImage(
-                      imageProvider,
-                      width: cacheWidth,
-                      height: cacheHeight,
-                    ),
-                  ),
-                ),
-              ),
-            );
+            return CachedNetworkImage(
+              fit: BoxFit.cover,
+              filterQuality: FilterQuality.high,
+              imageUrl: imageUrl,
+              memCacheWidth: cacheWidth,
+              memCacheHeight: cacheHeight,
+              errorWidget: (_, __, ___) => backupGradientAvatar,
+              placeholder: switch (effectivePlaceholder) {
+                final holder? => (context, __) => holder(context, user),
+                _ => null,
+              },
+              imageBuilder: (context, imageProvider) => DecoratedBox(
+                decoration: BoxDecoration(
+                  borderRadius: effectiveBorderRadius ?? BorderRadius.zero,
+                  image: DecorationImage(
+                    fit: BoxFit.cover,
+                    image: imageProvider,
+                  ),
+                ),
+              ),
+            );

If the new ThumbnailSizeCalculator utility in this PR is intended to be generic, consider reusing it here instead of open‑coding the size math so attachments and avatars stay in sync.

🧹 Nitpick comments (1)
packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart (1)

100-152: Avoid repeating the constraints expression for base and selected avatars

Lines 100 and 151 both resolve constraints ?? avatarTheme?.constraints. Not a problem, but you could simplify by computing an effectiveConstraints once and reusing it:

-    Widget avatar = FittedBox(
+    final effectiveConstraints = constraints ?? avatarTheme?.constraints;
+
+    Widget avatar = FittedBox(
       fit: BoxFit.cover,
       child: Container(
-        constraints: constraints ?? avatarTheme?.constraints,
+        constraints: effectiveConstraints,
@@
     if (selected) {
       avatar = ClipRRect(
@@
         child: Container(
-          constraints: constraints ?? avatarTheme?.constraints,
+          constraints: effectiveConstraints,

This keeps the layout logic in one place and makes it easier to adjust avatar sizing later.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9215fc1 and 08fee37.

📒 Files selected for processing (2)
  • packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart (3 hunks)
  • packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.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: analyze_legacy_versions
  • GitHub Check: stream_chat_flutter
  • GitHub Check: stream_chat_flutter_core
  • GitHub Check: stream_chat_localizations
  • GitHub Check: stream_chat_persistence
  • GitHub Check: build (android)
  • GitHub Check: stream_chat
  • GitHub Check: build (ios)
  • GitHub Check: test

Copy link
Contributor

@Brazol Brazol left a comment

Choose a reason for hiding this comment

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

LGTM

@xsahil03x xsahil03x merged commit 2147d2e into master Nov 18, 2025
24 of 25 checks passed
@xsahil03x xsahil03x deleted the fix/attachment-gallery-high-memory branch November 18, 2025 13:07
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.

Crash (ios): high watermark memory limit exceeded

3 participants