Skip to content

feat(ui): add StreamEmojiChip and StreamListTile#58

Open
xsahil03x wants to merge 4 commits intomain-design-systemfrom
feat/reactions-view-components
Open

feat(ui): add StreamEmojiChip and StreamListTile#58
xsahil03x wants to merge 4 commits intomain-design-systemfrom
feat/reactions-view-components

Conversation

@xsahil03x
Copy link
Member

@xsahil03x xsahil03x commented Feb 25, 2026

Screenshot 2026-02-25 at 17 27 35 Screenshot 2026-02-25 at 17 27 49

Summary by CodeRabbit

  • New Features

    • Added StreamEmojiChip component supporting emoji reactions with optional count and state management.
    • Added StreamListTile component for customizable list items with accessibility support.
    • Added theming support for emoji chips and list tiles via StreamTheme.
    • Added interactive component showcases and patterns in design system gallery.
    • Added baselines toggle to debug toolbar.
  • Improvements

    • Improved StreamEmoji rendering efficiency and layout.
    • Enhanced design system gallery documentation with setup and development guides.

@xsahil03x xsahil03x requested a review from a team as a code owner February 25, 2026 11:56
@xsahil03x
Copy link
Member Author

@coderabbitai review

1 similar comment
@xsahil03x
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

This PR introduces two new Flutter UI components (StreamEmojiChip and StreamListTile) to the stream-core-flutter package, including their default implementations, theming infrastructure, factory builders, and comprehensive gallery showcases demonstrating interactive use cases and state variants.

Changes

Cohort / File(s) Summary
Core Components - StreamEmojiChip
packages/stream_core_flutter/lib/src/components/controls/stream_emoji_chip.dart
Introduces StreamEmojiChip widget with props container, default implementation, and theme-aware styling. Supports emoji display, optional count badge, selection state, and onPressed/onLongPress callbacks via IconButton-based capsule.
Core Components - StreamListTile
packages/stream_core_flutter/lib/src/components/list/stream_list_tile.dart
Introduces StreamListTile widget with props container and default implementation. Renders configurable tile with optional leading/trailing, title/subtitle/description, enabled/selected states, and InkWell-based interaction.
Theme Infrastructure
packages/stream_core_flutter/lib/src/theme/components/stream_emoji_chip_theme.dart, stream_emoji_chip_theme.g.theme.dart, stream_list_tile_theme.dart, stream_list_tile_theme.g.theme.dart
Introduces InheritedTheme-based theming for both components with theme data containers, style properties (colors, text styles, sizing, padding, shapes), and generated lerp/copyWith/merge utilities.
Theme Integration
packages/stream_core_flutter/lib/src/theme/stream_theme.dart, stream_theme.g.theme.dart, stream_theme_extensions.dart
Extends global StreamTheme with emojiChipTheme and listTileTheme fields, adds context extension getters, and updates copyWith/lerp for both new themes.
Factory & Exports
packages/stream_core_flutter/lib/src/factory/stream_component_factory.dart, stream_component_factory.g.theme.dart, packages/stream_core_flutter/lib/src/components.dart, lib/src/theme.dart
Adds emojiChip and listTile builder hooks to StreamComponentBuilders for theming customization, updates generated factory code (copyWith, lerp, equality), and exports new components/themes from package entry points.
Gallery Showcase - StreamEmojiChip
apps/design_system_gallery/lib/components/controls/stream_emoji_chip.dart
Adds playground and showcase widgets demonstrating emoji chip variants, count values, state matrix (selected/disabled), real-world message reactions example with interactive toggle, and busy reaction panel with wrapping behavior.
Gallery Showcase - StreamListTile
apps/design_system_gallery/lib/components/tiles/stream_list_tile.dart
Adds playground and showcase widgets demonstrating tile states (default/selected/disabled), layout patterns (title-only, title+subtitle, full composition), and real-world conversation list example with selection and dividers.
Gallery Integration
apps/design_system_gallery/lib/app/gallery_app.directories.g.dart, lib/widgets/toolbar/baselines_toggle.dart, lib/widgets/toolbar/toolbar.dart
Registers StreamEmojiChip and StreamListTile showcase components in widgetbook directories, adds BaselinesToggle debug widget for toggling baseline rendering, integrates into toolbar debug controls.
Gallery Documentation
apps/design_system_gallery/README.md
Renames heading, replaces Getting Started with feature description and Flutter workflow, adds Quality Checks and component showcase instructions.
Minor Refinements
packages/stream_core_flutter/lib/src/components/accessories/stream_emoji.dart, lib/src/components/buttons/stream_emoji_button.dart
Simplifies emoji rendering by removing nested layout widgets, relies on DefaultTextStyle and text styling instead; adds tapTargetSize and visualDensity to emoji button.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • renefloor

Poem

🐰 Two components hop into the stream,
With chips that emoji and tiles that gleam,
Theming and galleries, a showcase so bright,
The design system dances with pure delight! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description contains only two screenshot images with no written explanation, missing all required template sections including CLA checkbox, description of changes, and testing instructions. Add written description explaining the changes, testing approach, and verification steps. Complete all required template sections including CLA checkbox and best practices confirmation.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(ui): add StreamEmojiChip and StreamListTile' directly and concisely describes the main change, accurately summarizing the addition of two new UI components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/reactions-view-components

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.

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

✅ Actions performed

Review triggered.

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

1 similar comment
@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

✅ Actions performed

Review triggered.

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
apps/design_system_gallery/lib/widgets/toolbar/baselines_toggle.dart (1)

15-18: Wrap the unawaited performReassemble() call with unawaited().

The unawaited_futures lint rule is enabled in the project, and performReassemble() returns Future<void>. Wrapping the call with unawaited() explicitly marks the ignored future and aligns with the established pattern used elsewhere in the codebase.

♻️ Proposed fix
+import 'dart:async';
 import 'package:flutter/material.dart';
 import 'package:flutter/rendering.dart';
  void _toggle() {
    setState(() => debugPaintBaselinesEnabled = !debugPaintBaselinesEnabled);
-   WidgetsBinding.instance.performReassemble();
+   unawaited(WidgetsBinding.instance.performReassemble());
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/design_system_gallery/lib/widgets/toolbar/baselines_toggle.dart` around
lines 15 - 18, The _toggle method currently calls
WidgetsBinding.instance.performReassemble() without awaiting; wrap that call
with unawaited(...) to satisfy the unawaited_futures lint and mark the future as
intentionally ignored. Update the _toggle implementation to call
unawaited(WidgetsBinding.instance.performReassemble()) and add the appropriate
import for unawaited (for example, import 'package:pedantic/pedantic.dart';) so
the symbol is available; keep the existing setState line toggling
debugPaintBaselinesEnabled unchanged.
apps/design_system_gallery/lib/components/controls/stream_emoji_chip.dart (1)

748-759: Consider adding error handling to _byName lookup for future-proofing.

All emoji names in _sampleEmojis currently exist in unicode_emojis 0.5.1's dataset. However, the _byName function uses firstWhere without an orElse clause, making initialization fragile if upstream unicode_emojis changes emoji names or structures in future versions. Adding a fallback improves robustness:

Suggested improvement
-Emoji _byName(String name) => UnicodeEmojis.allEmojis.firstWhere((e) => e.name == name);
+Emoji _byName(String name) => UnicodeEmojis.allEmojis.firstWhere(
+      (e) => e.name == name,
+      orElse: () => UnicodeEmojis.allEmojis.first,
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/design_system_gallery/lib/components/controls/stream_emoji_chip.dart`
around lines 748 - 759, The _byName function currently calls
UnicodeEmojis.allEmojis.firstWhere without an orElse, which can throw if a name
disappears; update _byName to handle missing lookups by using firstWhere(...,
orElse: () => <fallback>) or catching the StateError and returning a sensible
default emoji (or null) so _sampleEmojis initialization won’t crash — reference
the _byName function and the _sampleEmojis list when making this change.
packages/stream_core_flutter/lib/src/components/list/stream_list_tile.dart (1)

220-223: Force-unwraps on resolved state colors follow Flutter conventions but are worth documenting.

The ! on resolve(states) for title/subtitle/description/icon colors will throw at runtime if a custom theme resolves to null for any state combination. The defaults are safe, but a consumer-provided StreamListTileThemeData could trip this. Consider adding a brief dartdoc note on StreamListTileThemeData (or an assert) clarifying that these properties must resolve to non-null for all states.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stream_core_flutter/lib/src/components/list/stream_list_tile.dart`
around lines 220 - 223, The force-unwrapped resolves on theme colors (e.g., the
code computing effectiveTitleColor / effectiveSubtitleColor /
effectiveDescriptionColor / effectiveIconColor using (theme.titleColor ??
defaults.titleColor).resolve(states)!) can throw if a consumer-supplied
StreamListTileThemeData returns null for some states; add a short doc comment on
StreamListTileThemeData's color properties stating they must resolve to non-null
for all states and/or add defensive asserts where those properties are defined
(e.g., in StreamListTileThemeData constructor or its color getters) to enforce
non-null resolution at runtime so the resolve(...)! calls are safe.
packages/stream_core_flutter/lib/src/components/controls/stream_emoji_chip.dart (1)

210-219: Hardcoded icon size of 20 — consider deriving from the theme.

The _AddEmojiIcon uses a hardcoded size: 20, while the chip's iconSize (line 165) is StreamEmojiSize.sm.value. If these are meant to match, coupling them would prevent future drift. Minor since it's a private widget.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/stream_core_flutter/lib/src/components/controls/stream_emoji_chip.dart`
around lines 210 - 219, Replace the hardcoded IconThemeData(size: 20) in
_AddEmojiIcon.build with a derived size that matches the chip's iconSize (use
StreamEmojiSize.sm.value) so the add-reaction icon stays in sync; update the
IconThemeData to use StreamEmojiSize.sm.value (or pull the same constant from
wherever the chip uses it) and keep the rest of the widget unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/stream_core_flutter/lib/src/components/buttons/stream_emoji_button.dart`:
- Around line 169-170: The ButtonStyle in StreamEmojiButton currently uses
tapTargetSize: .shrinkWrap causing sizes below xl to violate the 48×48dp
touch-target minimum; update the style in the StreamEmojiButton widget (where
ButtonStyle is built) to ensure accessibility by either switching tapTargetSize
to MaterialTapTargetSize.padded for all buttons or, if you must keep shrinkWrap
visually, add conditional padding for smaller sizes (e.g., add
EdgeInsets.symmetric(horizontal/vertical ~8–12dp) when size != xl or when
diameter < 48) so that the effective touch target is at least 48×48dp; modify
the ButtonStyle construction (tapTargetSize and/or padding) accordingly in the
build/constructor that creates the emoji button.

In
`@packages/stream_core_flutter/lib/src/theme/components/stream_emoji_chip_theme.dart`:
- Around line 128-131: The constructor currently force-casts the parameter side
(type WidgetStateProperty<BorderSide?>?) to WidgetStateBorderSide? which will
throw for non-WidgetStateBorderSide inputs; update the constructor for safety by
either 1) changing the parameter type from WidgetStateProperty<BorderSide?>? to
WidgetStateBorderSide? (making the contract explicit and removing the cast), or
2) keeping the broader type and replacing the forced cast with a defensive
conversion: if side is WidgetStateBorderSide use it directly, else wrap
non-WidgetStateBorderSide instances (e.g. WidgetStatePropertyAll<BorderSide?>)
into a new WidgetStateBorderSide wrapper instance or convert them into an
equivalent WidgetStateBorderSide implementation before assigning the field;
update the constructor assignment for the field side accordingly and ensure any
downstream code expecting WidgetStateBorderSide handles the wrapped type.

---

Nitpick comments:
In `@apps/design_system_gallery/lib/components/controls/stream_emoji_chip.dart`:
- Around line 748-759: The _byName function currently calls
UnicodeEmojis.allEmojis.firstWhere without an orElse, which can throw if a name
disappears; update _byName to handle missing lookups by using firstWhere(...,
orElse: () => <fallback>) or catching the StateError and returning a sensible
default emoji (or null) so _sampleEmojis initialization won’t crash — reference
the _byName function and the _sampleEmojis list when making this change.

In `@apps/design_system_gallery/lib/widgets/toolbar/baselines_toggle.dart`:
- Around line 15-18: The _toggle method currently calls
WidgetsBinding.instance.performReassemble() without awaiting; wrap that call
with unawaited(...) to satisfy the unawaited_futures lint and mark the future as
intentionally ignored. Update the _toggle implementation to call
unawaited(WidgetsBinding.instance.performReassemble()) and add the appropriate
import for unawaited (for example, import 'package:pedantic/pedantic.dart';) so
the symbol is available; keep the existing setState line toggling
debugPaintBaselinesEnabled unchanged.

In
`@packages/stream_core_flutter/lib/src/components/controls/stream_emoji_chip.dart`:
- Around line 210-219: Replace the hardcoded IconThemeData(size: 20) in
_AddEmojiIcon.build with a derived size that matches the chip's iconSize (use
StreamEmojiSize.sm.value) so the add-reaction icon stays in sync; update the
IconThemeData to use StreamEmojiSize.sm.value (or pull the same constant from
wherever the chip uses it) and keep the rest of the widget unchanged.

In `@packages/stream_core_flutter/lib/src/components/list/stream_list_tile.dart`:
- Around line 220-223: The force-unwrapped resolves on theme colors (e.g., the
code computing effectiveTitleColor / effectiveSubtitleColor /
effectiveDescriptionColor / effectiveIconColor using (theme.titleColor ??
defaults.titleColor).resolve(states)!) can throw if a consumer-supplied
StreamListTileThemeData returns null for some states; add a short doc comment on
StreamListTileThemeData's color properties stating they must resolve to non-null
for all states and/or add defensive asserts where those properties are defined
(e.g., in StreamListTileThemeData constructor or its color getters) to enforce
non-null resolution at runtime so the resolve(...)! calls are safe.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between da18aa0 and 76d3526.

📒 Files selected for processing (22)
  • apps/design_system_gallery/README.md
  • apps/design_system_gallery/lib/app/gallery_app.directories.g.dart
  • apps/design_system_gallery/lib/components/controls/stream_emoji_chip.dart
  • apps/design_system_gallery/lib/components/tiles/stream_list_tile.dart
  • apps/design_system_gallery/lib/widgets/toolbar/baselines_toggle.dart
  • apps/design_system_gallery/lib/widgets/toolbar/toolbar.dart
  • packages/stream_core_flutter/lib/src/components.dart
  • packages/stream_core_flutter/lib/src/components/accessories/stream_emoji.dart
  • packages/stream_core_flutter/lib/src/components/buttons/stream_emoji_button.dart
  • packages/stream_core_flutter/lib/src/components/controls/stream_emoji_chip.dart
  • packages/stream_core_flutter/lib/src/components/list/stream_list_tile.dart
  • packages/stream_core_flutter/lib/src/factory/stream_component_factory.dart
  • packages/stream_core_flutter/lib/src/factory/stream_component_factory.g.theme.dart
  • packages/stream_core_flutter/lib/src/theme.dart
  • packages/stream_core_flutter/lib/src/theme/components/stream_emoji_chip_theme.dart
  • packages/stream_core_flutter/lib/src/theme/components/stream_emoji_chip_theme.g.theme.dart
  • packages/stream_core_flutter/lib/src/theme/components/stream_list_tile_theme.dart
  • packages/stream_core_flutter/lib/src/theme/components/stream_list_tile_theme.g.theme.dart
  • packages/stream_core_flutter/lib/src/theme/stream_theme.dart
  • packages/stream_core_flutter/lib/src/theme/stream_theme.g.theme.dart
  • packages/stream_core_flutter/lib/src/theme/stream_theme_extensions.dart
  • pubspec.yaml

Comment on lines +169 to +170
tapTargetSize: .shrinkWrap,
visualDensity: .standard,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find and examine the file
fd -t f "stream_emoji_button.dart"

Repository: GetStream/stream-core-flutter

Length of output: 228


🏁 Script executed:

# Get the file size first
wc -l packages/stream_core_flutter/lib/src/components/buttons/stream_emoji_button.dart

Repository: GetStream/stream-core-flutter

Length of output: 157


🏁 Script executed:

# Read the relevant section around lines 169-170 with context
sed -n '150,190p' packages/stream_core_flutter/lib/src/components/buttons/stream_emoji_button.dart

Repository: GetStream/stream-core-flutter

Length of output: 1769


🌐 Web query:

Flutter MaterialTapTargetSize shrinkWrap effect on touch target size interactive area

💡 Result:

MaterialTapTargetSize controls the minimum tap target (hit-test) size that certain Material widgets reserve, which affects both layout (extra empty space) and the interactive area (where a tap is accepted) for those widgets. [1]

  • MaterialTapTargetSize.padded (default): expands the minimum tap target to 48×48 logical pixels, so taps in the “invisible” padded area around the visual can still trigger the widget. This is intended to meet common accessibility guidance. [1]
  • MaterialTapTargetSize.shrinkWrap: removes that 48×48 expansion and shrinks the tap target down to the smaller minimum used by the Material spec / the widget’s own minimums, so the tappable area becomes much closer to the visual bounds (and can end up smaller than 48×48). [1][2]

Practical effect: if you set ThemeData.materialTapTargetSize (or a widget’s per-control tapTargetSize) to shrinkWrap, controls like IconButton, Checkbox, Radio, Switch, TextButton/ElevatedButton/OutlinedButton, Chip, etc. will often take less space and have a smaller hit region, which can hurt touch accessibility unless you add your own padding/constraints. [1][2]

Sources
[1] Flutter Dart API: MaterialTapTargetSize docs (padded = 48×48, shrinkWrap = smaller minimum) (api.flutter.dev)
[2] Example discussion/illustration of padded vs shrinkWrap changing the tappable empty space (stackoverflow.com)

Citations:


🌐 Web query:

Material Design minimum touch target size guidelines 48dp accessibility

💡 Result:

  • Minimum touch target (Material/Android accessibility): Make every interactive element’s touch target at least 48dp × 48dp (bigger is better). [1]
  • How to achieve it: The visible control can be smaller, as long as padding/min size makes the focusable/touchable area reach 48dp in both dimensions (e.g., via minWidth/minHeight + padding). [1]
  • Buttons specifically (Material Design): A button’s visual height can be 36dp, but the touchable target should still be 48dp minimum. [2]
  • Compose note: Android/Compose guidance also reiterates 48dp × 48dp as the minimum for clickable elements (many Material components apply this automatically). [3]

Sources: Android Developers accessibility guidance [1], Material Design buttons accessibility section [2], Android Developers accessibility testing codelab [3].

[1] (developer.android.com)
[2] (m1.material.io)
[3] (developer.android.com)

Citations:


🏁 Script executed:

# Find the StreamEmojiButtonSize enum or size definition
rg -t dart "enum StreamEmojiButtonSize|class StreamEmojiButtonSize" packages/stream_core_flutter/lib/src/components/buttons/

Repository: GetStream/stream-core-flutter

Length of output: 55


🏁 Script executed:

# Look for the defaults class and size definitions
sed -n '1,150p' packages/stream_core_flutter/lib/src/components/buttons/stream_emoji_button.dart | tail -80

Repository: GetStream/stream-core-flutter

Length of output: 2471


🏁 Script executed:

# Search for StreamEmojiSize to understand emoji size mapping
rg -t dart "class StreamEmojiSize|enum StreamEmojiSize" packages/stream_core_flutter/lib/src/

Repository: GetStream/stream-core-flutter

Length of output: 173


🏁 Script executed:

# Get the _StreamEmojiButtonThemeDefaults to see default sizes
rg -A 20 "_StreamEmojiButtonThemeDefaults" packages/stream_core_flutter/lib/src/components/buttons/stream_emoji_button.dart

Repository: GetStream/stream-core-flutter

Length of output: 1944


🏁 Script executed:

# Find StreamEmojiButtonSize enum definition
rg -t dart "enum StreamEmojiButtonSize|class StreamEmojiButtonSize" -A 15 packages/stream_core_flutter/

Repository: GetStream/stream-core-flutter

Length of output: 1798


🏁 Script executed:

# Search for StreamEmojiButtonSize definition in size-related files
fd -t f "size.dart|button.dart" packages/stream_core_flutter/lib/src | grep -i emoji

Repository: GetStream/stream-core-flutter

Length of output: 153


🏁 Script executed:

# Broader search for StreamEmojiButtonSize
rg -t dart "StreamEmojiButtonSize" packages/stream_core_flutter/lib/src/theme/ -A 2 -B 2

Repository: GetStream/stream-core-flutter

Length of output: 3930


Emoji button sizes below xl violate the 48×48dp touch target accessibility minimum when using shrinkWrap.

The ButtonStyle at lines 169–170 uses tapTargetSize: .shrinkWrap with padding: EdgeInsets.zero, which sets the touch target size equal to the visual button diameter. This causes:

  • xl (48px): meets minimum exactly, but with no safety margin
  • lg (40px): 40×40dp — below the 48×48dp minimum
  • md (32px): 32×32dp — below the 48×48dp minimum

Per Material Design and accessibility guidelines (48×48dp minimum), either restore tapTargetSize: .padded or add 8–12dp of padding for smaller sizes to meet touch target requirements.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/stream_core_flutter/lib/src/components/buttons/stream_emoji_button.dart`
around lines 169 - 170, The ButtonStyle in StreamEmojiButton currently uses
tapTargetSize: .shrinkWrap causing sizes below xl to violate the 48×48dp
touch-target minimum; update the style in the StreamEmojiButton widget (where
ButtonStyle is built) to ensure accessibility by either switching tapTargetSize
to MaterialTapTargetSize.padded for all buttons or, if you must keep shrinkWrap
visually, add conditional padding for smaller sizes (e.g., add
EdgeInsets.symmetric(horizontal/vertical ~8–12dp) when size != xl or when
diameter < 48) so that the effective touch target is at least 48×48dp; modify
the ButtonStyle construction (tapTargetSize and/or padding) accordingly in the
build/constructor that creates the emoji button.

Comment on lines +128 to +131
WidgetStateProperty<BorderSide?>? side,
})
// TODO: Fix this or try to find something better
: side = side as WidgetStateBorderSide?;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unsafe downcast of side will throw at runtime for non-WidgetStateBorderSide inputs.

The constructor accepts WidgetStateProperty<BorderSide?>? but force-casts to WidgetStateBorderSide? on line 131. Any caller passing a plain WidgetStatePropertyAll<BorderSide?> or a custom WidgetStateProperty<BorderSide?> will hit a TypeError.

Consider either:

  1. Narrowing the parameter type to WidgetStateBorderSide? directly (breaking the broader acceptance but making the contract explicit), or
  2. Adding a defensive check that wraps non-WidgetStateBorderSide values.
Option 1: Narrow the parameter type
  const StreamEmojiChipThemeStyle({
    this.backgroundColor,
    this.foregroundColor,
    this.overlayColor,
    this.textStyle,
    this.minimumSize,
    this.maximumSize,
    this.padding,
    this.shape,
-   WidgetStateProperty<BorderSide?>? side,
-  })
-    // TODO: Fix this or try to find something better
-    : side = side as WidgetStateBorderSide?;
+   this.side,
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
WidgetStateProperty<BorderSide?>? side,
})
// TODO: Fix this or try to find something better
: side = side as WidgetStateBorderSide?;
WidgetStateProperty<BorderSide?>? side,
})
// TODO: Fix this or try to find something better
: side = side as WidgetStateBorderSide?;
Suggested change
WidgetStateProperty<BorderSide?>? side,
})
// TODO: Fix this or try to find something better
: side = side as WidgetStateBorderSide?;
const StreamEmojiChipThemeStyle({
this.backgroundColor,
this.foregroundColor,
this.overlayColor,
this.textStyle,
this.minimumSize,
this.maximumSize,
this.padding,
this.shape,
this.side,
});
Suggested change
WidgetStateProperty<BorderSide?>? side,
})
// TODO: Fix this or try to find something better
: side = side as WidgetStateBorderSide?;
this.side,
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/stream_core_flutter/lib/src/theme/components/stream_emoji_chip_theme.dart`
around lines 128 - 131, The constructor currently force-casts the parameter side
(type WidgetStateProperty<BorderSide?>?) to WidgetStateBorderSide? which will
throw for non-WidgetStateBorderSide inputs; update the constructor for safety by
either 1) changing the parameter type from WidgetStateProperty<BorderSide?>? to
WidgetStateBorderSide? (making the contract explicit and removing the cast), or
2) keeping the broader type and replacing the forced cast with a defensive
conversion: if side is WidgetStateBorderSide use it directly, else wrap
non-WidgetStateBorderSide instances (e.g. WidgetStatePropertyAll<BorderSide?>)
into a new WidgetStateBorderSide wrapper instance or convert them into an
equivalent WidgetStateBorderSide implementation before assigning the field;
update the constructor assignment for the field side accordingly and ensure any
downstream code expecting WidgetStateBorderSide handles the wrapped type.

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.

1 participant