From d2b2a362054ed629372c7b0efa777a7d78420fc2 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Fri, 13 Mar 2026 17:31:50 +0100 Subject: [PATCH 1/7] make command chip --- .../devtools_options.yaml | 3 + .../lib/app/gallery_app.directories.g.dart | 25 ++- .../controls/stream_command_chip.dart | 139 ++++++++++++++++ .../lib/src/components.dart | 1 + .../controls/stream_command_chip.dart | 153 ++++++++++++++++++ .../message_composer_input.dart | 56 ++++--- .../src/factory/stream_component_factory.dart | 8 + .../stream_component_factory.g.theme.dart | 6 + .../stream_core_flutter/lib/src/theme.dart | 1 + .../components/stream_command_chip_theme.dart | 75 +++++++++ .../stream_command_chip_theme.g.theme.dart | 100 ++++++++++++ .../lib/src/theme/stream_theme.dart | 9 ++ .../lib/src/theme/stream_theme.g.theme.dart | 9 ++ .../src/theme/stream_theme_extensions.dart | 4 + .../stream_command_chip_golden_test.dart | 91 +++++++++++ 15 files changed, 658 insertions(+), 22 deletions(-) create mode 100644 apps/design_system_gallery/devtools_options.yaml create mode 100644 apps/design_system_gallery/lib/components/controls/stream_command_chip.dart create mode 100644 packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart create mode 100644 packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart create mode 100644 packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart create mode 100644 packages/stream_core_flutter/test/components/controls/stream_command_chip_golden_test.dart diff --git a/apps/design_system_gallery/devtools_options.yaml b/apps/design_system_gallery/devtools_options.yaml new file mode 100644 index 00000000..fa0b357c --- /dev/null +++ b/apps/design_system_gallery/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/apps/design_system_gallery/lib/app/gallery_app.directories.g.dart b/apps/design_system_gallery/lib/app/gallery_app.directories.g.dart index d6494aa3..b4a6e01e 100644 --- a/apps/design_system_gallery/lib/app/gallery_app.directories.g.dart +++ b/apps/design_system_gallery/lib/app/gallery_app.directories.g.dart @@ -40,6 +40,8 @@ import 'package:design_system_gallery/components/common/stream_progress_bar.dart as _design_system_gallery_components_common_stream_progress_bar; import 'package:design_system_gallery/components/context_menu/stream_context_menu.dart' as _design_system_gallery_components_context_menu_stream_context_menu; +import 'package:design_system_gallery/components/controls/stream_command_chip.dart' + as _design_system_gallery_components_controls_stream_command_chip; import 'package:design_system_gallery/components/controls/stream_emoji_chip.dart' as _design_system_gallery_components_controls_stream_emoji_chip; import 'package:design_system_gallery/components/controls/stream_emoji_chip_bar.dart' @@ -55,7 +57,7 @@ import 'package:design_system_gallery/components/message_composer/message_compos import 'package:design_system_gallery/components/message_composer/message_composer_attachment_reply.dart' as _design_system_gallery_components_message_composer_message_composer_attachment_reply; import 'package:design_system_gallery/components/reaction/stream_reactions.dart' - as _design_system_gallery_components_reaction_stream_reaction; + as _design_system_gallery_components_reaction_stream_reactions; import 'package:design_system_gallery/components/tiles/stream_list_tile.dart' as _design_system_gallery_components_tiles_stream_list_tile; import 'package:design_system_gallery/primitives/colors.dart' @@ -463,6 +465,23 @@ final directories = <_widgetbook.WidgetbookNode>[ _widgetbook.WidgetbookFolder( name: 'Controls', children: [ + _widgetbook.WidgetbookComponent( + name: 'StreamCommandChip', + useCases: [ + _widgetbook.WidgetbookUseCase( + name: 'Playground', + builder: + _design_system_gallery_components_controls_stream_command_chip + .buildStreamCommandChipPlayground, + ), + _widgetbook.WidgetbookUseCase( + name: 'Showcase', + builder: + _design_system_gallery_components_controls_stream_command_chip + .buildStreamCommandChipShowcase, + ), + ], + ), _widgetbook.WidgetbookComponent( name: 'StreamEmojiChip', useCases: [ @@ -579,13 +598,13 @@ final directories = <_widgetbook.WidgetbookNode>[ _widgetbook.WidgetbookUseCase( name: 'Playground', builder: - _design_system_gallery_components_reaction_stream_reaction + _design_system_gallery_components_reaction_stream_reactions .buildStreamReactionsPlayground, ), _widgetbook.WidgetbookUseCase( name: 'Showcase', builder: - _design_system_gallery_components_reaction_stream_reaction + _design_system_gallery_components_reaction_stream_reactions .buildStreamReactionsShowcase, ), ], diff --git a/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart b/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart new file mode 100644 index 00000000..ba1ac118 --- /dev/null +++ b/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart @@ -0,0 +1,139 @@ +import 'package:flutter/material.dart'; +import 'package:stream_core_flutter/stream_core_flutter.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +// ============================================================================= +// Playground +// ============================================================================= + +@widgetbook.UseCase( + name: 'Playground', + type: StreamCommandChip, + path: '[Components]/Controls', +) +Widget buildStreamCommandChipPlayground(BuildContext context) { + final label = context.knobs.string( + label: 'Label', + initialValue: '/giphy', + description: 'The command label displayed inside the chip.', + ); + + final enableDismiss = context.knobs.boolean( + label: 'On Dismiss', + initialValue: true, + description: 'Whether the dismiss callback is active.', + ); + + return Center( + child: StreamCommandChip( + label: label, + onDismiss: enableDismiss + ? () { + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + const SnackBar( + content: Text('Command dismissed'), + duration: Duration(seconds: 1), + ), + ); + } + : null, + ), + ); +} + +// ============================================================================= +// Showcase +// ============================================================================= + +@widgetbook.UseCase( + name: 'Showcase', + type: StreamCommandChip, + path: '[Components]/Controls', +) +Widget buildStreamCommandChipShowcase(BuildContext context) { + final colorScheme = context.streamColorScheme; + final spacing = context.streamSpacing; + final radius = context.streamRadius; + + return SingleChildScrollView( + padding: EdgeInsets.all(spacing.lg), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: spacing.xl, + children: [ + _SectionLabel(label: 'COMMAND CHIP — IMAGE OVERLAY'), + // Simulated attachment overlay + Stack( + alignment: Alignment.topLeft, + children: [ + Container( + width: 200, + height: 120, + decoration: BoxDecoration( + color: colorScheme.backgroundSurfaceSubtle, + borderRadius: BorderRadius.all(radius.md), + border: Border.all(color: colorScheme.borderSubtle), + ), + child: Center( + child: Icon(Icons.image, size: 48, color: colorScheme.textDisabled), + ), + ), + Padding( + padding: EdgeInsets.all(spacing.xs), + child: StreamCommandChip( + label: '/giphy', + onDismiss: () {}, + ), + ), + ], + ), + _SectionLabel(label: 'LABEL VARIANTS'), + Wrap( + spacing: spacing.xs, + runSpacing: spacing.xs, + children: [ + StreamCommandChip(label: '/giphy', onDismiss: () {}), + StreamCommandChip(label: '/img', onDismiss: () {}), + StreamCommandChip(label: '/ban', onDismiss: () {}), + StreamCommandChip(label: '/very-long-command-name', onDismiss: () {}), + ], + ), + _SectionLabel(label: 'WITHOUT DISMISS'), + StreamCommandChip(label: '/giphy'), + ], + ), + ); +} + +class _SectionLabel extends StatelessWidget { + const _SectionLabel({required this.label}); + + final String label; + + @override + Widget build(BuildContext context) { + final colorScheme = context.streamColorScheme; + final textTheme = context.streamTextTheme; + final radius = context.streamRadius; + final spacing = context.streamSpacing; + + return Container( + padding: EdgeInsets.symmetric(horizontal: spacing.sm, vertical: spacing.xs), + decoration: BoxDecoration( + color: colorScheme.accentPrimary, + borderRadius: BorderRadius.all(radius.xs), + ), + child: Text( + label, + style: textTheme.metadataEmphasis.copyWith( + color: colorScheme.textOnAccent, + letterSpacing: 1, + fontSize: 9, + ), + ), + ); + } +} diff --git a/packages/stream_core_flutter/lib/src/components.dart b/packages/stream_core_flutter/lib/src/components.dart index 07ad15cd..6a669def 100644 --- a/packages/stream_core_flutter/lib/src/components.dart +++ b/packages/stream_core_flutter/lib/src/components.dart @@ -15,6 +15,7 @@ export 'components/common/stream_flex.dart'; export 'components/common/stream_progress_bar.dart' hide DefaultStreamProgressBar; export 'components/context_menu/stream_context_menu.dart'; export 'components/context_menu/stream_context_menu_action.dart' hide DefaultStreamContextMenuAction; +export 'components/controls/stream_command_chip.dart' hide DefaultStreamCommandChip; export 'components/controls/stream_emoji_chip.dart' hide DefaultStreamEmojiChip; export 'components/controls/stream_emoji_chip_bar.dart' hide DefaultStreamEmojiChipBar; export 'components/controls/stream_remove_control.dart'; diff --git a/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart b/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart new file mode 100644 index 00000000..e5acb5d4 --- /dev/null +++ b/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart @@ -0,0 +1,153 @@ +import 'package:flutter/material.dart'; + +import '../../factory/stream_component_factory.dart'; +import '../../theme/stream_theme_extensions.dart'; + +/// A pill-shaped chip for displaying a slash command selection. +/// +/// [StreamCommandChip] renders a thunder icon, a command label, and a dismiss +/// button. It is typically used as an overlay on a message attachment when a +/// slash command is active in the message composer. +/// +/// {@tool snippet} +/// +/// Display a command chip with a dismiss callback: +/// +/// ```dart +/// StreamCommandChip( +/// label: '/giphy', +/// onDismiss: () => clearCommand(), +/// ) +/// ``` +/// {@end-tool} +/// +/// See also: +/// +/// * [StreamCommandChipTheme], for customizing chip appearance. +class StreamCommandChip extends StatelessWidget { + /// Creates a command chip with a [label] and optional [onDismiss] callback. + StreamCommandChip({ + super.key, + required String label, + VoidCallback? onDismiss, + }) : props = StreamCommandChipProps(label: label, onDismiss: onDismiss); + + /// The props controlling the appearance and behavior of this chip. + final StreamCommandChipProps props; + + @override + Widget build(BuildContext context) { + final builder = StreamComponentFactory.of(context).commandChip; + if (builder != null) return builder(context, props); + return DefaultStreamCommandChip(props: props); + } +} + +/// Properties for configuring a [StreamCommandChip]. +/// +/// See also: +/// +/// * [StreamCommandChip], which uses these properties. +/// * [DefaultStreamCommandChip], the default implementation. +class StreamCommandChipProps { + /// Creates properties for a command chip. + const StreamCommandChipProps({ + required this.label, + this.onDismiss, + }); + + /// The command label to display inside the chip. + final String label; + + /// Called when the dismiss (×) button is tapped. + /// + /// When null the dismiss button is still shown but does nothing. + final VoidCallback? onDismiss; +} + +/// Default implementation of [StreamCommandChip]. +class DefaultStreamCommandChip extends StatelessWidget { + /// Creates a default command chip. + const DefaultStreamCommandChip({super.key, required this.props}); + + /// The props controlling the appearance and behavior of this chip. + final StreamCommandChipProps props; + + @override + Widget build(BuildContext context) { + final defaults = _StreamCommandChipDefaults(context); + final chipTheme = context.streamCommandChipTheme; + + final effectiveBackgroundColor = chipTheme.backgroundColor ?? defaults.backgroundColor; + final effectiveLabelColor = chipTheme.labelColor ?? defaults.labelColor; + final effectiveIconColor = chipTheme.iconColor ?? defaults.iconColor; + + return Container( + padding: defaults.padding, + decoration: BoxDecoration( + color: effectiveBackgroundColor, + borderRadius: defaults.borderRadius, + ), + constraints: const BoxConstraints(minHeight: 24), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + context.streamIcons.thunder, + size: 12, + color: effectiveIconColor, + ), + SizedBox(width: defaults.spacing.xxxs), + MediaQuery.withNoTextScaling( + child: Text( + props.label, + style: defaults.labelStyle.copyWith(color: effectiveLabelColor), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + SizedBox(width: defaults.spacing.xxxs), + GestureDetector( + onTap: props.onDismiss, + behavior: HitTestBehavior.opaque, + child: SizedBox( + width: 16, + height: 16, + child: Icon( + context.streamIcons.crossMedium, + size: 12, + color: effectiveIconColor, + ), + ), + ), + ], + ), + ); + } +} + +// Provides default values for [StreamCommandChip] based on the current theme. +class _StreamCommandChipDefaults { + _StreamCommandChipDefaults(this._context); + + final BuildContext _context; + + late final _colorScheme = _context.streamColorScheme; + late final _textTheme = _context.streamTextTheme; + late final spacing = _context.streamSpacing; + + Color get backgroundColor => _colorScheme.backgroundInverse; + + Color get labelColor => _colorScheme.textInverse; + + Color get iconColor => _colorScheme.textInverse; + + TextStyle get labelStyle => _textTheme.metadataEmphasis; + + EdgeInsetsGeometry get padding => EdgeInsets.symmetric( + horizontal: spacing.xs, + vertical: spacing.xxxs, + ); + + BorderRadius get borderRadius => const BorderRadius.all(Radius.circular(9999)); +} diff --git a/packages/stream_core_flutter/lib/src/components/message_composer/message_composer_input.dart b/packages/stream_core_flutter/lib/src/components/message_composer/message_composer_input.dart index faf8f7e2..49084818 100644 --- a/packages/stream_core_flutter/lib/src/components/message_composer/message_composer_input.dart +++ b/packages/stream_core_flutter/lib/src/components/message_composer/message_composer_input.dart @@ -74,11 +74,15 @@ class StreamMessageComposerInputField extends StatelessWidget { required this.controller, required this.placeholder, this.focusNode, + this.command, + this.onDismissCommand, }); final TextEditingController controller; final String placeholder; final FocusNode? focusNode; + final String? command; + final VoidCallback? onDismissCommand; @override Widget build(BuildContext context) { @@ -93,26 +97,40 @@ class StreamMessageComposerInputField extends StatelessWidget { return ConstrainedBox( constraints: const BoxConstraints(maxHeight: 124), - child: TextField( - controller: controller, - focusNode: focusNode, - style: TextStyle( - color: inputTheme.textColor ?? inputDefaults.textColor, - ), - maxLines: null, - decoration: InputDecoration( - border: border, - focusedBorder: border, - enabledBorder: border, - errorBorder: border, - disabledBorder: border, - fillColor: Colors.transparent, - contentPadding: const EdgeInsets.fromLTRB(12, 8, 12, 8), - hintText: placeholder, - hintStyle: TextStyle( - color: inputTheme.placeholderColor ?? inputDefaults.placeholderColor, + child: Row( + children: [ + if (command case final command?) + Padding( + padding: EdgeInsets.only(left: context.streamSpacing.sm), + child: StreamCommandChip( + label: command, + onDismiss: onDismissCommand, + ), + ), + Expanded( + child: TextField( + controller: controller, + focusNode: focusNode, + style: TextStyle( + color: inputTheme.textColor ?? inputDefaults.textColor, + ), + maxLines: null, + decoration: InputDecoration( + border: border, + focusedBorder: border, + enabledBorder: border, + errorBorder: border, + disabledBorder: border, + fillColor: Colors.transparent, + contentPadding: EdgeInsets.fromLTRB(command == null ? 12 : 0, 8, 12, 8), + hintText: placeholder, + hintStyle: TextStyle( + color: inputTheme.placeholderColor ?? inputDefaults.placeholderColor, + ), + ), + ), ), - ), + ], ), ); } diff --git a/packages/stream_core_flutter/lib/src/factory/stream_component_factory.dart b/packages/stream_core_flutter/lib/src/factory/stream_component_factory.dart index a0c3a689..5caa76d3 100644 --- a/packages/stream_core_flutter/lib/src/factory/stream_component_factory.dart +++ b/packages/stream_core_flutter/lib/src/factory/stream_component_factory.dart @@ -138,6 +138,7 @@ class StreamComponentBuilders with _$StreamComponentBuilders { StreamComponentBuilder? badgeNotification, StreamComponentBuilder? button, StreamComponentBuilder? checkbox, + StreamComponentBuilder? commandChip, StreamComponentBuilder? contextMenuAction, StreamComponentBuilder? emoji, StreamComponentBuilder? emojiButton, @@ -160,6 +161,7 @@ class StreamComponentBuilders with _$StreamComponentBuilders { badgeNotification: badgeNotification, button: button, checkbox: checkbox, + commandChip: commandChip, contextMenuAction: contextMenuAction, emoji: emoji, emojiButton: emojiButton, @@ -183,6 +185,7 @@ class StreamComponentBuilders with _$StreamComponentBuilders { required this.badgeNotification, required this.button, required this.checkbox, + required this.commandChip, required this.contextMenuAction, required this.emoji, required this.emojiButton, @@ -251,6 +254,11 @@ class StreamComponentBuilders with _$StreamComponentBuilders { /// When null, [StreamCheckbox] uses [DefaultStreamCheckbox]. final StreamComponentBuilder? checkbox; + /// Custom builder for command chip widgets. + /// + /// When null, [StreamCommandChip] uses [DefaultStreamCommandChip]. + final StreamComponentBuilder? commandChip; + /// Custom builder for context menu action widgets. /// /// When null, [StreamContextMenuAction] uses [DefaultStreamContextMenuAction]. diff --git a/packages/stream_core_flutter/lib/src/factory/stream_component_factory.g.theme.dart b/packages/stream_core_flutter/lib/src/factory/stream_component_factory.g.theme.dart index 2e76e622..8e8e9732 100644 --- a/packages/stream_core_flutter/lib/src/factory/stream_component_factory.g.theme.dart +++ b/packages/stream_core_flutter/lib/src/factory/stream_component_factory.g.theme.dart @@ -38,6 +38,7 @@ mixin _$StreamComponentBuilders { badgeNotification: t < 0.5 ? a.badgeNotification : b.badgeNotification, button: t < 0.5 ? a.button : b.button, checkbox: t < 0.5 ? a.checkbox : b.checkbox, + commandChip: t < 0.5 ? a.commandChip : b.commandChip, contextMenuAction: t < 0.5 ? a.contextMenuAction : b.contextMenuAction, emoji: t < 0.5 ? a.emoji : b.emoji, emojiButton: t < 0.5 ? a.emojiButton : b.emojiButton, @@ -61,6 +62,7 @@ mixin _$StreamComponentBuilders { badgeNotification, Widget Function(BuildContext, StreamButtonProps)? button, Widget Function(BuildContext, StreamCheckboxProps)? checkbox, + Widget Function(BuildContext, StreamCommandChipProps)? commandChip, Widget Function(BuildContext, StreamContextMenuActionProps)? contextMenuAction, Widget Function(BuildContext, StreamEmojiProps)? emoji, @@ -85,6 +87,7 @@ mixin _$StreamComponentBuilders { badgeNotification: badgeNotification ?? _this.badgeNotification, button: button ?? _this.button, checkbox: checkbox ?? _this.checkbox, + commandChip: commandChip ?? _this.commandChip, contextMenuAction: contextMenuAction ?? _this.contextMenuAction, emoji: emoji ?? _this.emoji, emojiButton: emojiButton ?? _this.emojiButton, @@ -118,6 +121,7 @@ mixin _$StreamComponentBuilders { badgeNotification: other.badgeNotification, button: other.button, checkbox: other.checkbox, + commandChip: other.commandChip, contextMenuAction: other.contextMenuAction, emoji: other.emoji, emojiButton: other.emojiButton, @@ -152,6 +156,7 @@ mixin _$StreamComponentBuilders { _other.badgeNotification == _this.badgeNotification && _other.button == _this.button && _other.checkbox == _this.checkbox && + _other.commandChip == _this.commandChip && _other.contextMenuAction == _this.contextMenuAction && _other.emoji == _this.emoji && _other.emojiButton == _this.emojiButton && @@ -178,6 +183,7 @@ mixin _$StreamComponentBuilders { _this.badgeNotification, _this.button, _this.checkbox, + _this.commandChip, _this.contextMenuAction, _this.emoji, _this.emojiButton, diff --git a/packages/stream_core_flutter/lib/src/theme.dart b/packages/stream_core_flutter/lib/src/theme.dart index c4e82845..d73c589f 100644 --- a/packages/stream_core_flutter/lib/src/theme.dart +++ b/packages/stream_core_flutter/lib/src/theme.dart @@ -6,6 +6,7 @@ export 'theme/components/stream_badge_count_theme.dart'; export 'theme/components/stream_badge_notification_theme.dart'; export 'theme/components/stream_button_theme.dart'; export 'theme/components/stream_checkbox_theme.dart'; +export 'theme/components/stream_command_chip_theme.dart'; export 'theme/components/stream_context_menu_action_theme.dart'; export 'theme/components/stream_context_menu_theme.dart'; export 'theme/components/stream_emoji_button_theme.dart'; diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart new file mode 100644 index 00000000..87b5206f --- /dev/null +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:theme_extensions_builder_annotation/theme_extensions_builder_annotation.dart'; + +import '../stream_theme.dart'; + +part 'stream_command_chip_theme.g.theme.dart'; + +/// Applies a command chip theme to descendant command chip widgets. +/// +/// Wrap a subtree with [StreamCommandChipTheme] to override command chip +/// styling. Access the merged theme using [BuildContext.streamCommandChipTheme]. +/// +/// See also: +/// +/// * [StreamCommandChipThemeData], which describes the command chip theme. +class StreamCommandChipTheme extends InheritedTheme { + /// Creates a command chip theme that controls descendant command chips. + const StreamCommandChipTheme({ + super.key, + required this.data, + required super.child, + }); + + /// The command chip theme data for descendant widgets. + final StreamCommandChipThemeData data; + + /// Returns the [StreamCommandChipThemeData] from the current theme context. + /// + /// This merges the local theme (if any) with the global theme from + /// [StreamTheme]. + static StreamCommandChipThemeData of(BuildContext context) { + final localTheme = context.dependOnInheritedWidgetOfExactType(); + return StreamTheme.of(context).commandChipTheme.merge(localTheme?.data); + } + + @override + Widget wrap(BuildContext context, Widget child) { + return StreamCommandChipTheme(data: data, child: child); + } + + @override + bool updateShouldNotify(StreamCommandChipTheme oldWidget) => data != oldWidget.data; +} + +/// Theme data for customizing command chip widgets. +/// +/// See also: +/// +/// * [StreamCommandChipTheme], for overriding theme in a widget subtree. +@themeGen +@immutable +class StreamCommandChipThemeData with _$StreamCommandChipThemeData { + /// Creates a command chip theme with optional color overrides. + const StreamCommandChipThemeData({ + this.backgroundColor, + this.labelColor, + this.iconColor, + }); + + /// The background color of the chip. + final Color? backgroundColor; + + /// The color of the label text. + final Color? labelColor; + + /// The color of the leading and trailing icons. + final Color? iconColor; + + /// Linearly interpolate between two [StreamCommandChipThemeData] objects. + static StreamCommandChipThemeData? lerp( + StreamCommandChipThemeData? a, + StreamCommandChipThemeData? b, + double t, + ) => _$StreamCommandChipThemeData.lerp(a, b, t); +} diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart new file mode 100644 index 00000000..fb5d2a3f --- /dev/null +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart @@ -0,0 +1,100 @@ +// dart format width=80 +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, unused_element + +part of 'stream_command_chip_theme.dart'; + +// ************************************************************************** +// ThemeGenGenerator +// ************************************************************************** + +mixin _$StreamCommandChipThemeData { + bool get canMerge => true; + + static StreamCommandChipThemeData? lerp( + StreamCommandChipThemeData? a, + StreamCommandChipThemeData? b, + double t, + ) { + if (identical(a, b)) { + return a; + } + + if (a == null) { + return t == 1.0 ? b : null; + } + + if (b == null) { + return t == 0.0 ? a : null; + } + + return StreamCommandChipThemeData( + backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), + labelColor: Color.lerp(a.labelColor, b.labelColor, t), + iconColor: Color.lerp(a.iconColor, b.iconColor, t), + ); + } + + StreamCommandChipThemeData copyWith({ + Color? backgroundColor, + Color? labelColor, + Color? iconColor, + }) { + final _this = (this as StreamCommandChipThemeData); + + return StreamCommandChipThemeData( + backgroundColor: backgroundColor ?? _this.backgroundColor, + labelColor: labelColor ?? _this.labelColor, + iconColor: iconColor ?? _this.iconColor, + ); + } + + StreamCommandChipThemeData merge(StreamCommandChipThemeData? other) { + final _this = (this as StreamCommandChipThemeData); + + if (other == null || identical(_this, other)) { + return _this; + } + + if (!other.canMerge) { + return other; + } + + return copyWith( + backgroundColor: other.backgroundColor, + labelColor: other.labelColor, + iconColor: other.iconColor, + ); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + + if (other.runtimeType != runtimeType) { + return false; + } + + final _this = (this as StreamCommandChipThemeData); + final _other = (other as StreamCommandChipThemeData); + + return _other.backgroundColor == _this.backgroundColor && + _other.labelColor == _this.labelColor && + _other.iconColor == _this.iconColor; + } + + @override + int get hashCode { + final _this = (this as StreamCommandChipThemeData); + + return Object.hash( + runtimeType, + _this.backgroundColor, + _this.labelColor, + _this.iconColor, + ); + } +} diff --git a/packages/stream_core_flutter/lib/src/theme/stream_theme.dart b/packages/stream_core_flutter/lib/src/theme/stream_theme.dart index f0d35e2e..0f938e22 100644 --- a/packages/stream_core_flutter/lib/src/theme/stream_theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/stream_theme.dart @@ -10,6 +10,7 @@ import 'components/stream_badge_count_theme.dart'; import 'components/stream_badge_notification_theme.dart'; import 'components/stream_button_theme.dart'; import 'components/stream_checkbox_theme.dart'; +import 'components/stream_command_chip_theme.dart'; import 'components/stream_context_menu_action_theme.dart'; import 'components/stream_context_menu_theme.dart'; import 'components/stream_emoji_button_theme.dart'; @@ -100,6 +101,7 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { StreamBadgeNotificationThemeData? badgeNotificationTheme, StreamButtonThemeData? buttonTheme, StreamCheckboxThemeData? checkboxTheme, + StreamCommandChipThemeData? commandChipTheme, StreamContextMenuThemeData? contextMenuTheme, StreamContextMenuActionThemeData? contextMenuActionTheme, StreamEmojiButtonThemeData? emojiButtonTheme, @@ -132,6 +134,7 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { badgeNotificationTheme ??= const StreamBadgeNotificationThemeData(); buttonTheme ??= const StreamButtonThemeData(); checkboxTheme ??= const StreamCheckboxThemeData(); + commandChipTheme ??= const StreamCommandChipThemeData(); contextMenuTheme ??= const StreamContextMenuThemeData(); contextMenuActionTheme ??= const StreamContextMenuActionThemeData(); emojiButtonTheme ??= const StreamEmojiButtonThemeData(); @@ -158,6 +161,7 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { badgeNotificationTheme: badgeNotificationTheme, buttonTheme: buttonTheme, checkboxTheme: checkboxTheme, + commandChipTheme: commandChipTheme, contextMenuTheme: contextMenuTheme, contextMenuActionTheme: contextMenuActionTheme, emojiButtonTheme: emojiButtonTheme, @@ -198,6 +202,7 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { required this.badgeNotificationTheme, required this.buttonTheme, required this.checkboxTheme, + required this.commandChipTheme, required this.contextMenuTheme, required this.contextMenuActionTheme, required this.emojiButtonTheme, @@ -286,6 +291,9 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { /// The checkbox theme for this theme. final StreamCheckboxThemeData checkboxTheme; + /// The command chip theme for this theme. + final StreamCommandChipThemeData commandChipTheme; + /// The context menu theme for this theme. final StreamContextMenuThemeData contextMenuTheme; @@ -351,6 +359,7 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { badgeNotificationTheme: badgeNotificationTheme, buttonTheme: buttonTheme, checkboxTheme: checkboxTheme, + commandChipTheme: commandChipTheme, contextMenuTheme: contextMenuTheme, contextMenuActionTheme: contextMenuActionTheme, emojiButtonTheme: emojiButtonTheme, diff --git a/packages/stream_core_flutter/lib/src/theme/stream_theme.g.theme.dart b/packages/stream_core_flutter/lib/src/theme/stream_theme.g.theme.dart index 46d74ea5..e38c0a68 100644 --- a/packages/stream_core_flutter/lib/src/theme/stream_theme.g.theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/stream_theme.g.theme.dart @@ -26,6 +26,7 @@ mixin _$StreamTheme on ThemeExtension { StreamBadgeNotificationThemeData? badgeNotificationTheme, StreamButtonThemeData? buttonTheme, StreamCheckboxThemeData? checkboxTheme, + StreamCommandChipThemeData? commandChipTheme, StreamContextMenuThemeData? contextMenuTheme, StreamContextMenuActionThemeData? contextMenuActionTheme, StreamEmojiButtonThemeData? emojiButtonTheme, @@ -55,6 +56,7 @@ mixin _$StreamTheme on ThemeExtension { badgeNotificationTheme ?? _this.badgeNotificationTheme, buttonTheme: buttonTheme ?? _this.buttonTheme, checkboxTheme: checkboxTheme ?? _this.checkboxTheme, + commandChipTheme: commandChipTheme ?? _this.commandChipTheme, contextMenuTheme: contextMenuTheme ?? _this.contextMenuTheme, contextMenuActionTheme: contextMenuActionTheme ?? _this.contextMenuActionTheme, @@ -120,6 +122,11 @@ mixin _$StreamTheme on ThemeExtension { other.checkboxTheme, t, )!, + commandChipTheme: StreamCommandChipThemeData.lerp( + _this.commandChipTheme, + other.commandChipTheme, + t, + )!, contextMenuTheme: StreamContextMenuThemeData.lerp( _this.contextMenuTheme, other.contextMenuTheme, @@ -192,6 +199,7 @@ mixin _$StreamTheme on ThemeExtension { _other.badgeNotificationTheme == _this.badgeNotificationTheme && _other.buttonTheme == _this.buttonTheme && _other.checkboxTheme == _this.checkboxTheme && + _other.commandChipTheme == _this.commandChipTheme && _other.contextMenuTheme == _this.contextMenuTheme && _other.contextMenuActionTheme == _this.contextMenuActionTheme && _other.emojiButtonTheme == _this.emojiButtonTheme && @@ -224,6 +232,7 @@ mixin _$StreamTheme on ThemeExtension { _this.badgeNotificationTheme, _this.buttonTheme, _this.checkboxTheme, + _this.commandChipTheme, _this.contextMenuTheme, _this.contextMenuActionTheme, _this.emojiButtonTheme, diff --git a/packages/stream_core_flutter/lib/src/theme/stream_theme_extensions.dart b/packages/stream_core_flutter/lib/src/theme/stream_theme_extensions.dart index 3321b728..110e64f5 100644 --- a/packages/stream_core_flutter/lib/src/theme/stream_theme_extensions.dart +++ b/packages/stream_core_flutter/lib/src/theme/stream_theme_extensions.dart @@ -6,6 +6,7 @@ import 'components/stream_badge_count_theme.dart'; import 'components/stream_badge_notification_theme.dart'; import 'components/stream_button_theme.dart'; import 'components/stream_checkbox_theme.dart'; +import 'components/stream_command_chip_theme.dart'; import 'components/stream_context_menu_action_theme.dart'; import 'components/stream_context_menu_theme.dart'; import 'components/stream_emoji_button_theme.dart'; @@ -88,6 +89,9 @@ extension StreamThemeExtension on BuildContext { /// Returns the [StreamCheckboxThemeData] from the nearest ancestor. StreamCheckboxThemeData get streamCheckboxTheme => StreamCheckboxTheme.of(this); + /// Returns the [StreamCommandChipThemeData] from the nearest ancestor. + StreamCommandChipThemeData get streamCommandChipTheme => StreamCommandChipTheme.of(this); + /// Returns the [StreamContextMenuThemeData] from the nearest ancestor. StreamContextMenuThemeData get streamContextMenuTheme => StreamContextMenuTheme.of(this); diff --git a/packages/stream_core_flutter/test/components/controls/stream_command_chip_golden_test.dart b/packages/stream_core_flutter/test/components/controls/stream_command_chip_golden_test.dart new file mode 100644 index 00000000..8b23d5af --- /dev/null +++ b/packages/stream_core_flutter/test/components/controls/stream_command_chip_golden_test.dart @@ -0,0 +1,91 @@ +// ignore_for_file: avoid_redundant_argument_values + +import 'package:alchemist/alchemist.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stream_core_flutter/stream_core_flutter.dart'; + +void main() { + group('StreamCommandChip Golden Tests', () { + goldenTest( + 'renders light theme', + fileName: 'stream_command_chip_light', + builder: () => GoldenTestGroup( + scenarioConstraints: const BoxConstraints(maxWidth: 300), + children: [ + GoldenTestScenario( + name: 'short label', + child: _buildInTheme( + StreamCommandChip(label: '/giphy', onDismiss: () {}), + ), + ), + GoldenTestScenario( + name: 'long label', + child: _buildInTheme( + StreamCommandChip(label: '/very-long-command-name', onDismiss: () {}), + ), + ), + GoldenTestScenario( + name: 'no dismiss', + child: _buildInTheme( + StreamCommandChip(label: '/giphy'), + ), + ), + ], + ), + ); + + goldenTest( + 'renders dark theme', + fileName: 'stream_command_chip_dark', + builder: () => GoldenTestGroup( + scenarioConstraints: const BoxConstraints(maxWidth: 300), + children: [ + GoldenTestScenario( + name: 'short label', + child: _buildInTheme( + StreamCommandChip(label: '/giphy', onDismiss: () {}), + brightness: Brightness.dark, + ), + ), + GoldenTestScenario( + name: 'long label', + child: _buildInTheme( + StreamCommandChip(label: '/very-long-command-name', onDismiss: () {}), + brightness: Brightness.dark, + ), + ), + GoldenTestScenario( + name: 'no dismiss', + child: _buildInTheme( + StreamCommandChip(label: '/giphy'), + brightness: Brightness.dark, + ), + ), + ], + ), + ); + }); +} + +Widget _buildInTheme( + Widget chip, { + Brightness brightness = Brightness.light, +}) { + final streamTheme = StreamTheme(brightness: brightness); + return Theme( + data: ThemeData( + brightness: brightness, + extensions: [streamTheme], + ), + child: Builder( + builder: (context) => Material( + color: StreamTheme.of(context).colorScheme.backgroundApp, + child: Padding( + padding: const EdgeInsets.all(8), + child: chip, + ), + ), + ), + ); +} From 188939dcb7408e9a94e1dd628a92637af598f430 Mon Sep 17 00:00:00 2001 From: renefloor <15101411+renefloor@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:45:37 +0000 Subject: [PATCH 2/7] chore: Update Goldens --- .../goldens/ci/stream_command_chip_dark.png | Bin 0 -> 2781 bytes .../goldens/ci/stream_command_chip_light.png | Bin 0 -> 2815 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/stream_core_flutter/test/components/controls/goldens/ci/stream_command_chip_dark.png create mode 100644 packages/stream_core_flutter/test/components/controls/goldens/ci/stream_command_chip_light.png diff --git a/packages/stream_core_flutter/test/components/controls/goldens/ci/stream_command_chip_dark.png b/packages/stream_core_flutter/test/components/controls/goldens/ci/stream_command_chip_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..154259292acc8c7d89ac394357fbb5e2271d6842 GIT binary patch literal 2781 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 literal 0 HcmV?d00001 diff --git a/packages/stream_core_flutter/test/components/controls/goldens/ci/stream_command_chip_light.png b/packages/stream_core_flutter/test/components/controls/goldens/ci/stream_command_chip_light.png new file mode 100644 index 0000000000000000000000000000000000000000..fa502a37e4c0922e99ce43f73e6228d6a9f5ad9d GIT binary patch literal 2815 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 literal 0 HcmV?d00001 From 38d8636e08ee5b9ec129f498ace4ff1468c8b1ed Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Tue, 17 Mar 2026 10:30:17 +0100 Subject: [PATCH 3/7] Add option without dismiss --- .../controls/stream_command_chip.dart | 6 ++--- .../controls/stream_command_chip.dart | 26 ++++++++++--------- .../stream_command_chip_golden_test.dart | 2 -- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart b/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart index ba1ac118..03dd2ad3 100644 --- a/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart +++ b/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart @@ -64,7 +64,7 @@ Widget buildStreamCommandChipShowcase(BuildContext context) { crossAxisAlignment: CrossAxisAlignment.start, spacing: spacing.xl, children: [ - _SectionLabel(label: 'COMMAND CHIP — IMAGE OVERLAY'), + const _SectionLabel(label: 'COMMAND CHIP — IMAGE OVERLAY'), // Simulated attachment overlay Stack( alignment: Alignment.topLeft, @@ -90,7 +90,7 @@ Widget buildStreamCommandChipShowcase(BuildContext context) { ), ], ), - _SectionLabel(label: 'LABEL VARIANTS'), + const _SectionLabel(label: 'LABEL VARIANTS'), Wrap( spacing: spacing.xs, runSpacing: spacing.xs, @@ -101,7 +101,7 @@ Widget buildStreamCommandChipShowcase(BuildContext context) { StreamCommandChip(label: '/very-long-command-name', onDismiss: () {}), ], ), - _SectionLabel(label: 'WITHOUT DISMISS'), + const _SectionLabel(label: 'WITHOUT DISMISS'), StreamCommandChip(label: '/giphy'), ], ), diff --git a/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart b/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart index e5acb5d4..7632e986 100644 --- a/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart +++ b/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart @@ -106,20 +106,22 @@ class DefaultStreamCommandChip extends StatelessWidget { maxLines: 1, ), ), - SizedBox(width: defaults.spacing.xxxs), - GestureDetector( - onTap: props.onDismiss, - behavior: HitTestBehavior.opaque, - child: SizedBox( - width: 16, - height: 16, - child: Icon( - context.streamIcons.crossMedium, - size: 12, - color: effectiveIconColor, + if (props.onDismiss != null) ...[ + SizedBox(width: defaults.spacing.xxxs), + GestureDetector( + onTap: props.onDismiss, + behavior: HitTestBehavior.opaque, + child: SizedBox( + width: 16, + height: 16, + child: Icon( + context.streamIcons.crossMedium, + size: 12, + color: effectiveIconColor, + ), ), ), - ), + ], ], ), ); diff --git a/packages/stream_core_flutter/test/components/controls/stream_command_chip_golden_test.dart b/packages/stream_core_flutter/test/components/controls/stream_command_chip_golden_test.dart index 8b23d5af..f4723e62 100644 --- a/packages/stream_core_flutter/test/components/controls/stream_command_chip_golden_test.dart +++ b/packages/stream_core_flutter/test/components/controls/stream_command_chip_golden_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_redundant_argument_values - import 'package:alchemist/alchemist.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; From 3527403617499e1624abb092b7d3d25c45620c37 Mon Sep 17 00:00:00 2001 From: renefloor <15101411+renefloor@users.noreply.github.com> Date: Tue, 17 Mar 2026 09:31:30 +0000 Subject: [PATCH 4/7] chore: Update Goldens --- .../goldens/ci/stream_command_chip_dark.png | Bin 2781 -> 2775 bytes .../goldens/ci/stream_command_chip_light.png | Bin 2815 -> 2811 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/packages/stream_core_flutter/test/components/controls/goldens/ci/stream_command_chip_dark.png b/packages/stream_core_flutter/test/components/controls/goldens/ci/stream_command_chip_dark.png index 154259292acc8c7d89ac394357fbb5e2271d6842..9b77e011e43a563e00f17e6f69cbd57750aa0ca2 100644 GIT binary patch delta 2403 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 delta 2415 zcmZ{ld0f(48peNMtCihKtqfDko4Zyn6``fMR)&U@7O0cg5UpIoeWB==KR4Xok5gm32cL>E2-GY!xvh`Ky&D{Nt*<2Gc8uEx?PNdO(kLqD11GV=@&9jO<({9H2S3N2yooKEqX?^)6W8}Ai z!M2za)sbwmsm~WgB<{;wrhZ>d+{|0R&`*NOoSR|GITL(hEg8B-e`yKN-L}GPWST@7 zVB!JbWJ)Rwaxi)2keZfq+)9Ob^DPgmhMbPxBdQ1)-9gUm2f}HF zero7McFarbDUoJ1o4AdSL zRaCe&H#fh}YlZsw_^9pP?HU+Z3Wvi9-#%{PCVRJhe7Gl6I%F;Bt43y8T<;LDGO-R& zsHvG5jY`#uo$tZ<2L^I^JYI_6GH0jH{^a}TvJr*34v&fo3jCZPA|qW1Dp{E1e~6p!tjAehrw7G|H?~8d185GWu@r* z_lt4(LwUkQ5=8WuKfBR+-2X3;UVFZ|3zaK*#gFrrfNn>Q%1HhZj=tZ;NVGJ7COY*blQ z<=<6=#;c1LO=(T>TMd#O4G#>GN?y^g;&2K%rfLsGOA9kag@x^HZD)sOnkce?Xe8L+ zZ5B=fv%YbO%(ZfYhm71mprg~(-#@Xq%gcZ!68UEx55J+;Gu>Tvy^k>bVXca?at)D8 z@_p>6O&Dp}#ytd}JR?U{vW^CSkVSwM5eT}A-!N6UqEd~y4y(}A;11MBqg`F!t3(_F z$LVNNKijdNm`%QR%pob0v=SS;Fq$^^e!a%{%JQP#*y&oE1lxTvN9$~}$Ar9cAG(is z28YWv*#4k~3`w7z?L;od85}42YMGmvNg>yPE9JY`KIEd};+yJ_gRcq;KXGdw-zAKH zrD{%()K>;@{biGUx{szeXpY|Mz#Q?eTX>VVQql!FCL;v%*{1hhY_vwVL^o{WCn%LnW_tDW&J!YPvwz1_FCARORo}nQ@9nzv0=PBo5h$?`$&1ajZ>FF(J zQxFHOtR~@5=*I=fUo*?H!F*Wi>!g%Vzqv$C53n+iU>#02-iwb1r5%N@^X>&LXl`kl zwR*WoBH5yk^32T6ewh^VwZH$_yQS$f;5<96ZAI_yzJP6rvn*TiZUt4I{kSJ^X>#2^Yoo}TN7gPc z{ty+HiqgE|R*QWH@7%eAm+%tgpq;tILR3D6*?4$S+O)N+yITN0?DOLHhEn8-WsWRj zPaYnOwY}Yy>zbqms)#aF?G$$A*WOwi896-b#x{4QvdwhQx9^IuHyjYPpZPJL*?<>(K_DXB+|}ed@4m}m4Urm_sd-hw3lkcu)kRRV7|J*V$52% z-$lIzVQ}I1Stg6v-tSWk&Dh%bA#T?~vCRpjd>p?1ml|nK^}55z_xz1~Y`vZA;%gfy zWlxS41UUOV2>=fKRyO(^2&ZFRhk;VUQfkFL8D4&5S{Y0O!h5 z4?1DsEu%yH-niyRN0$y1u9?OK>DjBXzLa(KXKHCF2=w5KRi@8X*^YFQ$(kC*WMhXoN!j^t-79RjH!7} zB0;&XK%?2;;6^7n$rzHQA{%G+q4T>k?w;1*tQ zQz4aVojvAzz*#A=`3m9@Al;z1N%)m|D#Iz|TQ?nB%&!Zt14nx`&yWTZ0(Zb^Z$U{3 z2ppj8w1;E2G5gC^L>eGn%yGx-n}cAy}8ztX^j0FGl&a-=FSDiM08M)i_W=c?ki@;3K|-`^3P?3s4D<% zGqERVRaF*X0M>>fX&9`B^ZBi*x~loSga?4g gL>3y7JgK274ZJ5$&1osf=mEgxWOvS%ZsSe*7iN|1ga7~l diff --git a/packages/stream_core_flutter/test/components/controls/goldens/ci/stream_command_chip_light.png b/packages/stream_core_flutter/test/components/controls/goldens/ci/stream_command_chip_light.png index fa502a37e4c0922e99ce43f73e6228d6a9f5ad9d..b9ff4d029382b8af63e7e68224935cba7a3f9cd0 100644 GIT binary patch literal 2811 zcmb`Jdo+~$8pmI^Vw6;JSL~8BlD5zu8?zCk+PREzpBWJ;Mi&h+Mucd~Xv;`0n?e|u zNs;S_*lxpUD1$M%<~F1-gK;}=XRW=@*=wD(JL|0T{_(!=Z~fl&e4l5%&+qen?%ABT z+zC;H006Ml3Sn*s075z7`+f0k;9a+gGXOqB15B;##lb67{9-J)77DPlGzCf+%F_S< z$+a^7$^KUQ9Ea$K;;FQJ>B1RyMt!f^9YZ%$)+fJez29yo*ANITySp84EWOz$st&ml zDKqJIO*cNS>!z;Wj_hNtk%$TSHMpw_WejPup8Sm+V6wp>Fy=)q5^BHG0w&47hDFy2D9Ei(Mj!Dq^(eCiI*s)C zcIr?i^uah@8~_g8GZh9>;A%p^xvU2OaM%U`0DF%d0>UL^_5k&w5DCCtI9e3YiTK|? zv%5R~h>#(q1I;b`=}~-Y@mcMvLGBUw8_%e3#f^fWQkEj92yd0o#3%4ZpC#0iA_7DS z{HfKv{oPTSj>PXbI%Bfs z9N}N(mE7Iav#pF4hL&f)$J>#~g=Jxlhk1Oy)Dig8HaeZzox5x1S>m)Ul^$KnUs`_C zXBH#HeSB#vzqgFhx@V_swBntNFWS7yo-C=|?Dg$&Dp zfH+z0PWe={u*0!J^5=Ui9o#^c48 z;JOA*+-Z`DTQ(-L6&ho9A%j?0?(~Y`@jGW*L{Z2ZI#)dWvTMagC-cT`$;;f{Isfr5 zGzLRkPmeZ_-%(jqQ)5{Z^m+`xqrR-UIS4<|@i>-#S}P~A^O(Qaf)U?q^oNk8lzDFr zp{M&Gc_MU5(Q8w*^L z(+nA0`D@N#;fJk%oBEoWl7FkCs&s6nTa)Qgt&|Ce!&s|-6^cyKu=hz zN&nXhHKD#m5dZOTrP%TDi^ni!Z@KcZzq(${$R25o304u&(bUy7)4h~@%B7d-7WnZa zU60tToMB895E9g9YF+(}slkh%-bP7`q1pTW6n?z*AZ)qsYT1(TUTKXPuoK4lL)-(u zt)#q>Oz zyLc;Ouj8*|E>Jt~9&uGn zScps}U%~S-$w@MtK}p-LcQ$TrIh>trl)b(D9Hmajug|9V)}~dlGwqjA{u8)rmipOG ztTb2sqlSib?kTvN*QHC-oyU~Qi;BvyT*J-QPi_tF&oJo4#MBRK@HM{v<*iK-SjcSU z`Sd9p>#l+@u_~wB+;|(El!Em1!ke6OR(@q=tZqpbciv}R!;W)UV*$7?fzEj7NCpn47~6Z6alN{Np)H%El1*?`3HYxqx1&sl0f1sX=F z&zMUWsydi)F8)9!7k>!&TpqU1&XHw> zawzE*>`Z&dW^=3T+>@6+x`12-NWGL1FJlVmM0xn-CluwwV_K>zzcDNkS~ zGgnUg2?KSb zD+g;jU0)SEecD})9sCg3SB|T?FrGiVwJ|slVjd@ZoSEFIL{5eE^|%4UawH1`VOhVR zXqMTRcOobEBa%bF76=4_e})?NiLyu09EilB^yN|?OV}jIyomksQl^v&tV7`zAiaIJ zB?67sKsmCcGfy^0HC`xo-Ku7t0P5vHZ2jD+9o;yxl5--zq@;n>bpRMu_fW%es*Q}T z?$6B5+97+^K>UVBfdG@zuV~V^cVSiph^2ujM9977%dG)`M?xD)2smu6l~Y!>`-_!_ z(||Z%2xxL?GHs{R?^*9@?As1V6F_j8+_no%lqaXAQU(XF{}B`-`RsR%d$!Th$bI6X zwIm_1nLX6Jyy8&}@yM}<%_)CE4j7gsO}u~}$Cga%PE0LL_Y4tcJyO%OT^1)ePbBs> zgMI;oHZU&_IXipumXIGytvy>SulI-etsVrmfT3%Dh0{VSh!?a{ujBROhk)4ZfAL3R z!vg*9`~ilcelF^%M%f%KHNhkAGy+PJ-Eb+(3P6id)~lOC$|u14*g{r&qx0^m+3VQ};Xn E0C`UoUjP6A literal 2815 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 From a95a16ff5a0796d50889fa44bc9908dcddd81c49 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Tue, 17 Mar 2026 11:49:18 +0100 Subject: [PATCH 5/7] Minor PR improvements --- .../components/controls/stream_command_chip.dart | 14 ++++++++------ .../components/stream_command_chip_theme.dart | 4 ++++ .../stream_command_chip_theme.g.theme.dart | 8 +++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart b/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart index 7632e986..047e08b0 100644 --- a/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart +++ b/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart @@ -81,6 +81,7 @@ class DefaultStreamCommandChip extends StatelessWidget { final effectiveBackgroundColor = chipTheme.backgroundColor ?? defaults.backgroundColor; final effectiveLabelColor = chipTheme.labelColor ?? defaults.labelColor; final effectiveIconColor = chipTheme.iconColor ?? defaults.iconColor; + final effectiveMinHeight = chipTheme.minHeight ?? defaults.minHeight; return Container( padding: defaults.padding, @@ -88,16 +89,16 @@ class DefaultStreamCommandChip extends StatelessWidget { color: effectiveBackgroundColor, borderRadius: defaults.borderRadius, ), - constraints: const BoxConstraints(minHeight: 24), + constraints: BoxConstraints(minHeight: effectiveMinHeight), child: Row( mainAxisSize: MainAxisSize.min, + spacing: defaults.spacing.xxxs, children: [ Icon( context.streamIcons.thunder, size: 12, color: effectiveIconColor, ), - SizedBox(width: defaults.spacing.xxxs), MediaQuery.withNoTextScaling( child: Text( props.label, @@ -106,8 +107,7 @@ class DefaultStreamCommandChip extends StatelessWidget { maxLines: 1, ), ), - if (props.onDismiss != null) ...[ - SizedBox(width: defaults.spacing.xxxs), + if (props.onDismiss != null) GestureDetector( onTap: props.onDismiss, behavior: HitTestBehavior.opaque, @@ -121,7 +121,6 @@ class DefaultStreamCommandChip extends StatelessWidget { ), ), ), - ], ], ), ); @@ -137,6 +136,7 @@ class _StreamCommandChipDefaults { late final _colorScheme = _context.streamColorScheme; late final _textTheme = _context.streamTextTheme; late final spacing = _context.streamSpacing; + late final _radius = _context.streamRadius; Color get backgroundColor => _colorScheme.backgroundInverse; @@ -146,10 +146,12 @@ class _StreamCommandChipDefaults { TextStyle get labelStyle => _textTheme.metadataEmphasis; + double get minHeight => 24; + EdgeInsetsGeometry get padding => EdgeInsets.symmetric( horizontal: spacing.xs, vertical: spacing.xxxs, ); - BorderRadius get borderRadius => const BorderRadius.all(Radius.circular(9999)); + BorderRadius get borderRadius => BorderRadius.all(_radius.max); } diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart index 87b5206f..0369ca2b 100644 --- a/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart @@ -55,6 +55,7 @@ class StreamCommandChipThemeData with _$StreamCommandChipThemeData { this.backgroundColor, this.labelColor, this.iconColor, + this.minHeight, }); /// The background color of the chip. @@ -66,6 +67,9 @@ class StreamCommandChipThemeData with _$StreamCommandChipThemeData { /// The color of the leading and trailing icons. final Color? iconColor; + /// The minimum height of the chip. + final double? minHeight; + /// Linearly interpolate between two [StreamCommandChipThemeData] objects. static StreamCommandChipThemeData? lerp( StreamCommandChipThemeData? a, diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart index fb5d2a3f..e8ee4f3d 100644 --- a/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart @@ -33,6 +33,7 @@ mixin _$StreamCommandChipThemeData { backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), labelColor: Color.lerp(a.labelColor, b.labelColor, t), iconColor: Color.lerp(a.iconColor, b.iconColor, t), + minHeight: lerpDouble$(a.minHeight, b.minHeight, t), ); } @@ -40,6 +41,7 @@ mixin _$StreamCommandChipThemeData { Color? backgroundColor, Color? labelColor, Color? iconColor, + double? minHeight, }) { final _this = (this as StreamCommandChipThemeData); @@ -47,6 +49,7 @@ mixin _$StreamCommandChipThemeData { backgroundColor: backgroundColor ?? _this.backgroundColor, labelColor: labelColor ?? _this.labelColor, iconColor: iconColor ?? _this.iconColor, + minHeight: minHeight ?? _this.minHeight, ); } @@ -65,6 +68,7 @@ mixin _$StreamCommandChipThemeData { backgroundColor: other.backgroundColor, labelColor: other.labelColor, iconColor: other.iconColor, + minHeight: other.minHeight, ); } @@ -83,7 +87,8 @@ mixin _$StreamCommandChipThemeData { return _other.backgroundColor == _this.backgroundColor && _other.labelColor == _this.labelColor && - _other.iconColor == _this.iconColor; + _other.iconColor == _this.iconColor && + _other.minHeight == _this.minHeight; } @override @@ -95,6 +100,7 @@ mixin _$StreamCommandChipThemeData { _this.backgroundColor, _this.labelColor, _this.iconColor, + _this.minHeight, ); } } From b8a04c3940e118831b96debcde554185820053fa Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Tue, 17 Mar 2026 11:51:34 +0100 Subject: [PATCH 6/7] update gallery labels --- .../components/controls/stream_command_chip.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart b/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart index 03dd2ad3..76c5286e 100644 --- a/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart +++ b/apps/design_system_gallery/lib/components/controls/stream_command_chip.dart @@ -15,7 +15,7 @@ import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; Widget buildStreamCommandChipPlayground(BuildContext context) { final label = context.knobs.string( label: 'Label', - initialValue: '/giphy', + initialValue: 'GIPHY', description: 'The command label displayed inside the chip.', ); @@ -84,7 +84,7 @@ Widget buildStreamCommandChipShowcase(BuildContext context) { Padding( padding: EdgeInsets.all(spacing.xs), child: StreamCommandChip( - label: '/giphy', + label: 'GIPHY', onDismiss: () {}, ), ), @@ -95,14 +95,14 @@ Widget buildStreamCommandChipShowcase(BuildContext context) { spacing: spacing.xs, runSpacing: spacing.xs, children: [ - StreamCommandChip(label: '/giphy', onDismiss: () {}), - StreamCommandChip(label: '/img', onDismiss: () {}), - StreamCommandChip(label: '/ban', onDismiss: () {}), - StreamCommandChip(label: '/very-long-command-name', onDismiss: () {}), + StreamCommandChip(label: 'GIPHY', onDismiss: () {}), + StreamCommandChip(label: 'IMG', onDismiss: () {}), + StreamCommandChip(label: 'BAN', onDismiss: () {}), + StreamCommandChip(label: 'VERY LONG COMMAND NAME', onDismiss: () {}), ], ), const _SectionLabel(label: 'WITHOUT DISMISS'), - StreamCommandChip(label: '/giphy'), + StreamCommandChip(label: 'GIPHY'), ], ), ); From bd9e5266f687d4cb0f4bd32b5c0cd49bd52eec1c Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Tue, 17 Mar 2026 12:11:49 +0100 Subject: [PATCH 7/7] add missing theme properties --- .../controls/stream_command_chip.dart | 22 ++++++---- .../components/stream_command_chip_theme.dart | 20 ++++++--- .../stream_command_chip_theme.g.theme.dart | 42 +++++++++++++------ 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart b/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart index 047e08b0..6416a5bc 100644 --- a/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart +++ b/packages/stream_core_flutter/lib/src/components/controls/stream_command_chip.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../../factory/stream_component_factory.dart'; +import '../../theme/components/stream_command_chip_theme.dart'; import '../../theme/stream_theme_extensions.dart'; /// A pill-shaped chip for displaying a slash command selection. @@ -79,8 +80,7 @@ class DefaultStreamCommandChip extends StatelessWidget { final chipTheme = context.streamCommandChipTheme; final effectiveBackgroundColor = chipTheme.backgroundColor ?? defaults.backgroundColor; - final effectiveLabelColor = chipTheme.labelColor ?? defaults.labelColor; - final effectiveIconColor = chipTheme.iconColor ?? defaults.iconColor; + final effectiveForegroundColor = chipTheme.foregroundColor ?? defaults.foregroundColor; final effectiveMinHeight = chipTheme.minHeight ?? defaults.minHeight; return Container( @@ -97,12 +97,12 @@ class DefaultStreamCommandChip extends StatelessWidget { Icon( context.streamIcons.thunder, size: 12, - color: effectiveIconColor, + color: effectiveForegroundColor, ), MediaQuery.withNoTextScaling( child: Text( props.label, - style: defaults.labelStyle.copyWith(color: effectiveLabelColor), + style: defaults.labelStyle.copyWith(color: effectiveForegroundColor), overflow: TextOverflow.ellipsis, maxLines: 1, ), @@ -117,7 +117,7 @@ class DefaultStreamCommandChip extends StatelessWidget { child: Icon( context.streamIcons.crossMedium, size: 12, - color: effectiveIconColor, + color: effectiveForegroundColor, ), ), ), @@ -128,7 +128,7 @@ class DefaultStreamCommandChip extends StatelessWidget { } // Provides default values for [StreamCommandChip] based on the current theme. -class _StreamCommandChipDefaults { +class _StreamCommandChipDefaults extends StreamCommandChipThemeData { _StreamCommandChipDefaults(this._context); final BuildContext _context; @@ -138,20 +138,24 @@ class _StreamCommandChipDefaults { late final spacing = _context.streamSpacing; late final _radius = _context.streamRadius; + @override Color get backgroundColor => _colorScheme.backgroundInverse; - Color get labelColor => _colorScheme.textInverse; - - Color get iconColor => _colorScheme.textInverse; + @override + Color get foregroundColor => _colorScheme.textInverse; + @override TextStyle get labelStyle => _textTheme.metadataEmphasis; + @override double get minHeight => 24; + @override EdgeInsetsGeometry get padding => EdgeInsets.symmetric( horizontal: spacing.xs, vertical: spacing.xxxs, ); + @override BorderRadius get borderRadius => BorderRadius.all(_radius.max); } diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart index 0369ca2b..55f64c88 100644 --- a/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.dart @@ -53,23 +53,31 @@ class StreamCommandChipThemeData with _$StreamCommandChipThemeData { /// Creates a command chip theme with optional color overrides. const StreamCommandChipThemeData({ this.backgroundColor, - this.labelColor, - this.iconColor, + this.foregroundColor, this.minHeight, + this.labelStyle, + this.padding, + this.borderRadius, }); /// The background color of the chip. final Color? backgroundColor; - /// The color of the label text. - final Color? labelColor; + /// The color of the icon and label text. + final Color? foregroundColor; - /// The color of the leading and trailing icons. - final Color? iconColor; + /// The style of the label text. + final TextStyle? labelStyle; /// The minimum height of the chip. final double? minHeight; + /// The padding of the chip. + final EdgeInsetsGeometry? padding; + + /// The border radius of the chip. + final BorderRadiusGeometry? borderRadius; + /// Linearly interpolate between two [StreamCommandChipThemeData] objects. static StreamCommandChipThemeData? lerp( StreamCommandChipThemeData? a, diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart index e8ee4f3d..6fa58839 100644 --- a/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_command_chip_theme.g.theme.dart @@ -31,25 +31,35 @@ mixin _$StreamCommandChipThemeData { return StreamCommandChipThemeData( backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), - labelColor: Color.lerp(a.labelColor, b.labelColor, t), - iconColor: Color.lerp(a.iconColor, b.iconColor, t), + foregroundColor: Color.lerp(a.foregroundColor, b.foregroundColor, t), + labelStyle: TextStyle.lerp(a.labelStyle, b.labelStyle, t), minHeight: lerpDouble$(a.minHeight, b.minHeight, t), + padding: EdgeInsetsGeometry.lerp(a.padding, b.padding, t), + borderRadius: BorderRadiusGeometry.lerp( + a.borderRadius, + b.borderRadius, + t, + ), ); } StreamCommandChipThemeData copyWith({ Color? backgroundColor, - Color? labelColor, - Color? iconColor, + Color? foregroundColor, + TextStyle? labelStyle, double? minHeight, + EdgeInsetsGeometry? padding, + BorderRadiusGeometry? borderRadius, }) { final _this = (this as StreamCommandChipThemeData); return StreamCommandChipThemeData( backgroundColor: backgroundColor ?? _this.backgroundColor, - labelColor: labelColor ?? _this.labelColor, - iconColor: iconColor ?? _this.iconColor, + foregroundColor: foregroundColor ?? _this.foregroundColor, + labelStyle: labelStyle ?? _this.labelStyle, minHeight: minHeight ?? _this.minHeight, + padding: padding ?? _this.padding, + borderRadius: borderRadius ?? _this.borderRadius, ); } @@ -66,9 +76,11 @@ mixin _$StreamCommandChipThemeData { return copyWith( backgroundColor: other.backgroundColor, - labelColor: other.labelColor, - iconColor: other.iconColor, + foregroundColor: other.foregroundColor, + labelStyle: _this.labelStyle?.merge(other.labelStyle) ?? other.labelStyle, minHeight: other.minHeight, + padding: other.padding, + borderRadius: other.borderRadius, ); } @@ -86,9 +98,11 @@ mixin _$StreamCommandChipThemeData { final _other = (other as StreamCommandChipThemeData); return _other.backgroundColor == _this.backgroundColor && - _other.labelColor == _this.labelColor && - _other.iconColor == _this.iconColor && - _other.minHeight == _this.minHeight; + _other.foregroundColor == _this.foregroundColor && + _other.labelStyle == _this.labelStyle && + _other.minHeight == _this.minHeight && + _other.padding == _this.padding && + _other.borderRadius == _this.borderRadius; } @override @@ -98,9 +112,11 @@ mixin _$StreamCommandChipThemeData { return Object.hash( runtimeType, _this.backgroundColor, - _this.labelColor, - _this.iconColor, + _this.foregroundColor, + _this.labelStyle, _this.minHeight, + _this.padding, + _this.borderRadius, ); } }