From 34b9f62b23c1d6574de7967bcf6863a0df2f8702 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Tue, 27 Jan 2026 15:53:26 +0100 Subject: [PATCH 01/12] improvements on old button component --- .../lib/app/gallery_app.directories.g.dart | 41 + .../lib/components/button.dart | 822 +++++++++--------- .../lib/config/theme_configuration.dart | 2 +- .../lib/primitives/colors.dart | 4 +- .../lib/src/components.dart | 2 +- .../src/components/buttons/stream_button.dart | 196 +++++ .../lib/src/factory/components.dart | 1 - .../src/factory/components/stream_button.dart | 160 ---- .../components/stream_button_theme.dart | 13 - .../src/factory/stream_component_factory.dart | 2 +- .../lib/src/factory/stream_theme.dart | 43 - .../theme/components/stream_button_theme.dart | 61 ++ .../stream_button_theme.g.theme.dart | 237 +++++ .../stream_color_swatch_helper.dart | 70 +- .../src/theme/primitives/stream_colors.dart | 313 ++++--- .../primitives/tokens/dark/stream_tokens.dart | 572 ++++++++++++ .../tokens/light/stream_tokens.dart | 572 ++++++++++++ .../tokens/stream_tokens_typography.dart | 114 +++ .../theme/semantics/stream_color_scheme.dart | 56 +- .../stream_color_scheme.g.theme.dart | 6 + .../lib/src/theme/stream_theme.dart | 16 + .../lib/src/theme/stream_theme.g.theme.dart | 14 +- .../src/theme/stream_theme_extensions.dart | 4 + 23 files changed, 2525 insertions(+), 796 deletions(-) create mode 100644 packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart delete mode 100644 packages/stream_core_flutter/lib/src/factory/components.dart delete mode 100644 packages/stream_core_flutter/lib/src/factory/components/stream_button.dart delete mode 100644 packages/stream_core_flutter/lib/src/factory/components/stream_button_theme.dart delete mode 100644 packages/stream_core_flutter/lib/src/factory/stream_theme.dart create mode 100644 packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart create mode 100644 packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart create mode 100644 packages/stream_core_flutter/lib/src/theme/primitives/tokens/dark/stream_tokens.dart create mode 100644 packages/stream_core_flutter/lib/src/theme/primitives/tokens/light/stream_tokens.dart create mode 100644 packages/stream_core_flutter/lib/src/theme/primitives/tokens/stream_tokens_typography.dart 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 55d133c..16d774e 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 @@ -10,6 +10,8 @@ // ************************************************************************** // ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:design_system_gallery/components/button.dart' + as _design_system_gallery_components_button; import 'package:design_system_gallery/components/stream_avatar.dart' as _design_system_gallery_components_stream_avatar; import 'package:design_system_gallery/components/stream_avatar_stack.dart' @@ -31,6 +33,7 @@ import 'package:widgetbook/widgetbook.dart' as _widgetbook; final directories = <_widgetbook.WidgetbookNode>[ _widgetbook.WidgetbookCategory( name: 'App Foundation', + isInitiallyExpanded: false, children: [ _widgetbook.WidgetbookFolder( name: 'Primitives', @@ -124,6 +127,7 @@ final directories = <_widgetbook.WidgetbookNode>[ children: [ _widgetbook.WidgetbookFolder( name: 'Avatar', + isInitiallyExpanded: false, children: [ _widgetbook.WidgetbookComponent( name: 'StreamAvatar', @@ -157,8 +161,45 @@ final directories = <_widgetbook.WidgetbookNode>[ ), ], ), + _widgetbook.WidgetbookFolder( + name: 'Button', + isInitiallyExpanded: false, + children: [ + _widgetbook.WidgetbookComponent( + name: 'StreamButton', + useCases: [ + _widgetbook.WidgetbookUseCase( + name: 'Playground', + builder: _design_system_gallery_components_button + .buildStreamButtonPlayground, + ), + _widgetbook.WidgetbookUseCase( + name: 'Real-world Example', + builder: _design_system_gallery_components_button + .buildStreamButtonExample, + ), + _widgetbook.WidgetbookUseCase( + name: 'Size Variants', + builder: _design_system_gallery_components_button + .buildStreamButtonSizes, + ), + _widgetbook.WidgetbookUseCase( + name: 'Type Variants', + builder: _design_system_gallery_components_button + .buildStreamButtonTypes, + ), + _widgetbook.WidgetbookUseCase( + name: 'With Icons', + builder: _design_system_gallery_components_button + .buildStreamButtonWithIcons, + ), + ], + ), + ], + ), _widgetbook.WidgetbookFolder( name: 'Indicator', + isInitiallyExpanded: false, children: [ _widgetbook.WidgetbookComponent( name: 'StreamOnlineIndicator', diff --git a/apps/design_system_gallery/lib/components/button.dart b/apps/design_system_gallery/lib/components/button.dart index 9974881..320c521 100644 --- a/apps/design_system_gallery/lib/components/button.dart +++ b/apps/design_system_gallery/lib/components/button.dart @@ -1,389 +1,433 @@ -// 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: StreamButton, -// path: '[Components]/Button', -// ) -// Widget buildStreamButtonPlayground(BuildContext context) { -// final label = context.knobs.string( -// label: 'Label', -// initialValue: 'Click me', -// description: 'The text displayed on the button.', -// ); -// -// final type = context.knobs.object.dropdown( -// label: 'Type', -// options: StreamButtonType.values, -// initialOption: StreamButtonType.primary, -// labelBuilder: (option) => option.name, -// description: 'Button visual style variant.', -// ); -// -// final size = context.knobs.object.dropdown( -// label: 'Size', -// options: StreamButtonSize.values, -// initialOption: StreamButtonSize.large, -// labelBuilder: (option) => option.name, -// description: 'Button size preset (affects padding and font size).', -// ); -// -// final isDisabled = context.knobs.boolean( -// label: 'Disabled', -// description: 'Whether the button is disabled (non-interactive).', -// ); -// -// final showLeadingIcon = context.knobs.boolean( -// label: 'Leading Icon', -// description: 'Show an icon before the label.', -// ); -// -// final showTrailingIcon = context.knobs.boolean( -// label: 'Trailing Icon', -// description: 'Show an icon after the label.', -// ); -// -// return Center( -// child: StreamButton( -// label: label, -// type: type, -// size: size, -// onTap: isDisabled -// ? null -// : () { -// ScaffoldMessenger.of(context).showSnackBar( -// const SnackBar( -// content: Text('Button tapped!'), -// duration: Duration(seconds: 1), -// ), -// ); -// }, -// iconLeft: showLeadingIcon ? const Icon(Icons.add) : null, -// iconRight: showTrailingIcon ? const Icon(Icons.arrow_forward) : null, -// ), -// ); -// } -// -// // ============================================================================= -// // Type Variants -// // ============================================================================= -// -// @widgetbook.UseCase( -// name: 'Type Variants', -// type: StreamButton, -// path: '[Components]/Button', -// ) -// Widget buildStreamButtonTypes(BuildContext context) { -// final theme = StreamTheme.of(context); -// final colorScheme = theme.colorScheme; -// final textTheme = theme.textTheme; -// -// return Center( -// child: Container( -// padding: const EdgeInsets.all(24), -// decoration: BoxDecoration( -// color: colorScheme.backgroundSurface, -// borderRadius: BorderRadius.circular(12), -// ), -// child: Column( -// mainAxisSize: MainAxisSize.min, -// children: [ -// for (final type in StreamButtonType.values) ...[ -// Row( -// mainAxisSize: MainAxisSize.min, -// children: [ -// SizedBox( -// width: 100, -// child: Text( -// type.name, -// style: textTheme.captionDefault.copyWith( -// color: colorScheme.textSecondary, -// ), -// ), -// ), -// StreamButton( -// label: 'Button', -// type: type, -// onTap: () {}, -// ), -// ], -// ), -// if (type != StreamButtonType.values.last) const SizedBox(height: 16), -// ], -// ], -// ), -// ), -// ); -// } -// -// // ============================================================================= -// // Size Variants -// // ============================================================================= -// -// @widgetbook.UseCase( -// name: 'Size Variants', -// type: StreamButton, -// path: '[Components]/Button', -// ) -// Widget buildStreamButtonSizes(BuildContext context) { -// final theme = StreamTheme.of(context); -// final colorScheme = theme.colorScheme; -// final textTheme = theme.textTheme; -// -// return Center( -// child: Container( -// padding: const EdgeInsets.all(24), -// decoration: BoxDecoration( -// color: colorScheme.backgroundSurface, -// borderRadius: BorderRadius.circular(12), -// ), -// child: Column( -// mainAxisSize: MainAxisSize.min, -// children: [ -// for (final size in StreamButtonSize.values) ...[ -// Row( -// mainAxisSize: MainAxisSize.min, -// children: [ -// SizedBox( -// width: 80, -// child: Text( -// size.name, -// style: textTheme.captionDefault.copyWith( -// color: colorScheme.textSecondary, -// ), -// ), -// ), -// StreamButton( -// label: 'Button', -// size: size, -// onTap: () {}, -// ), -// ], -// ), -// if (size != StreamButtonSize.values.last) const SizedBox(height: 16), -// ], -// ], -// ), -// ), -// ); -// } -// -// // ============================================================================= -// // With Icons -// // ============================================================================= -// -// @widgetbook.UseCase( -// name: 'With Icons', -// type: StreamButton, -// path: '[Components]/Button', -// ) -// Widget buildStreamButtonWithIcons(BuildContext context) { -// final theme = StreamTheme.of(context); -// final colorScheme = theme.colorScheme; -// final textTheme = theme.textTheme; -// -// return Center( -// child: Container( -// padding: const EdgeInsets.all(24), -// decoration: BoxDecoration( -// color: colorScheme.backgroundSurface, -// borderRadius: BorderRadius.circular(12), -// ), -// child: Column( -// mainAxisSize: MainAxisSize.min, -// children: [ -// Row( -// mainAxisSize: MainAxisSize.min, -// children: [ -// SizedBox( -// width: 80, -// child: Text( -// 'Leading', -// style: textTheme.captionDefault.copyWith( -// color: colorScheme.textSecondary, -// ), -// ), -// ), -// StreamButton( -// label: 'Add Item', -// iconLeft: const Icon(Icons.add), -// onTap: () {}, -// ), -// ], -// ), -// const SizedBox(height: 16), -// Row( -// mainAxisSize: MainAxisSize.min, -// children: [ -// SizedBox( -// width: 80, -// child: Text( -// 'Trailing', -// style: textTheme.captionDefault.copyWith( -// color: colorScheme.textSecondary, -// ), -// ), -// ), -// StreamButton( -// label: 'Continue', -// iconRight: const Icon(Icons.arrow_forward), -// onTap: () {}, -// ), -// ], -// ), -// const SizedBox(height: 16), -// Row( -// mainAxisSize: MainAxisSize.min, -// children: [ -// SizedBox( -// width: 80, -// child: Text( -// 'Both', -// style: textTheme.captionDefault.copyWith( -// color: colorScheme.textSecondary, -// ), -// ), -// ), -// StreamButton( -// label: 'Upload', -// iconLeft: const Icon(Icons.cloud_upload), -// iconRight: const Icon(Icons.arrow_forward), -// onTap: () {}, -// ), -// ], -// ), -// ], -// ), -// ), -// ); -// } -// -// // ============================================================================= -// // Real-world Example -// // ============================================================================= -// -// @widgetbook.UseCase( -// name: 'Real-world Example', -// type: StreamButton, -// path: '[Components]/Button', -// ) -// Widget buildStreamButtonExample(BuildContext context) { -// final theme = StreamTheme.of(context); -// final colorScheme = theme.colorScheme; -// final textTheme = theme.textTheme; -// -// return Center( -// child: Container( -// padding: const EdgeInsets.all(24), -// decoration: BoxDecoration( -// color: colorScheme.backgroundSurface, -// borderRadius: BorderRadius.circular(12), -// ), -// child: Column( -// mainAxisSize: MainAxisSize.min, -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Text( -// 'Common Patterns', -// style: textTheme.headingSm.copyWith( -// color: colorScheme.textPrimary, -// ), -// ), -// const SizedBox(height: 16), -// // Dialog actions -// Container( -// padding: const EdgeInsets.all(16), -// width: 280, -// clipBehavior: Clip.antiAlias, -// decoration: BoxDecoration( -// color: colorScheme.backgroundApp, -// borderRadius: BorderRadius.circular(8), -// ), -// foregroundDecoration: BoxDecoration( -// borderRadius: BorderRadius.circular(8), -// border: Border.all(color: colorScheme.borderSurfaceSubtle), -// ), -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// mainAxisSize: MainAxisSize.min, -// children: [ -// Text( -// 'Delete conversation?', -// style: textTheme.bodyEmphasis.copyWith( -// color: colorScheme.textPrimary, -// ), -// ), -// const SizedBox(height: 4), -// Text( -// 'This action cannot be undone.', -// style: textTheme.captionDefault.copyWith( -// color: colorScheme.textSecondary, -// ), -// ), -// const SizedBox(height: 16), -// Row( -// mainAxisAlignment: MainAxisAlignment.end, -// children: [ -// StreamButton( -// label: 'Cancel', -// type: StreamButtonType.secondary, -// size: StreamButtonSize.small, -// onTap: () {}, -// ), -// const SizedBox(width: 8), -// StreamButton( -// label: 'Delete', -// type: StreamButtonType.destructive, -// size: StreamButtonSize.small, -// onTap: () {}, -// ), -// ], -// ), -// ], -// ), -// ), -// const SizedBox(height: 12), -// // Form submit -// Container( -// padding: const EdgeInsets.all(16), -// width: 280, -// clipBehavior: Clip.antiAlias, -// decoration: BoxDecoration( -// color: colorScheme.backgroundApp, -// borderRadius: BorderRadius.circular(8), -// ), -// foregroundDecoration: BoxDecoration( -// borderRadius: BorderRadius.circular(8), -// border: Border.all(color: colorScheme.borderSurfaceSubtle), -// ), -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.stretch, -// mainAxisSize: MainAxisSize.min, -// children: [ -// Text( -// 'Ready to send?', -// style: textTheme.bodyEmphasis.copyWith( -// color: colorScheme.textPrimary, -// ), -// ), -// const SizedBox(height: 12), -// StreamButton( -// label: 'Send Message', -// iconRight: const Icon(Icons.send), -// onTap: () {}, -// ), -// ], -// ), -// ), -// ], -// ), -// ), -// ); -// } +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: StreamButton, + path: '[Components]/Button', +) +Widget buildStreamButtonPlayground(BuildContext context) { + final label = context.knobs.string( + label: 'Label', + initialValue: 'Click me', + description: 'The text displayed on the button.', + ); + + final style = context.knobs.object.dropdown( + label: 'Style', + options: StreamButtonStyle.values, + initialOption: StreamButtonStyle.primary, + labelBuilder: (option) => option.name, + description: 'Button visual style variant.', + ); + + final type = context.knobs.object.dropdown( + label: 'Type', + options: StreamButtonType.values, + initialOption: StreamButtonType.solid, + labelBuilder: (option) => option.name, + description: 'Button type variant.', + ); + + final size = context.knobs.object.dropdown( + label: 'Size', + options: StreamButtonSize.values, + initialOption: StreamButtonSize.large, + labelBuilder: (option) => option.name, + description: 'Button size preset (affects padding and font size).', + ); + + final isDisabled = context.knobs.boolean( + label: 'Disabled', + description: 'Whether the button is disabled (non-interactive).', + ); + + final showLeadingIcon = context.knobs.boolean( + label: 'Leading Icon', + description: 'Show an icon before the label.', + ); + + final showTrailingIcon = context.knobs.boolean( + label: 'Trailing Icon', + description: 'Show an icon after the label.', + ); + + return Center( + child: Padding( + padding: const EdgeInsets.all(8), + child: StreamButton( + label: label, + style: style, + type: type, + size: size, + onTap: isDisabled + ? null + : () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Button tapped!'), + duration: Duration(seconds: 1), + ), + ); + }, + iconLeft: showLeadingIcon ? Icons.add : null, + iconRight: showTrailingIcon ? Icons.arrow_forward : null, + ), + ), + ); +} + +// ============================================================================= +// Type Variants +// ============================================================================= + +@widgetbook.UseCase( + name: 'Type Variants', + type: StreamButton, + path: '[Components]/Button', +) +Widget buildStreamButtonTypes(BuildContext context) { + final theme = StreamTheme.of(context); + final colorScheme = theme.colorScheme; + final textTheme = theme.textTheme; + + return Center( + child: Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: colorScheme.backgroundSurface, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final type in StreamButtonType.values) ...[ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 100, + child: Text( + type.name, + style: textTheme.captionDefault.copyWith( + color: colorScheme.textSecondary, + ), + ), + ), + StreamButton( + label: 'Button', + type: type, + onTap: () {}, + ), + ], + ), + if (type != StreamButtonType.values.last) const SizedBox(height: 16), + ], + ], + ), + ), + ); +} + +// ============================================================================= +// Size Variants +// ============================================================================= + +@widgetbook.UseCase( + name: 'Size Variants', + type: StreamButton, + path: '[Components]/Button', +) +Widget buildStreamButtonSizes(BuildContext context) { + final theme = StreamTheme.of(context); + final colorScheme = theme.colorScheme; + final textTheme = theme.textTheme; + + return Center( + child: Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: colorScheme.backgroundSurface, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final size in StreamButtonSize.values) ...[ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 80, + child: Text( + size.name, + style: textTheme.captionDefault.copyWith( + color: colorScheme.textSecondary, + ), + ), + ), + StreamButton( + label: 'Button', + size: size, + onTap: () {}, + ), + ], + ), + if (size != StreamButtonSize.values.last) const SizedBox(height: 16), + ], + ], + ), + ), + ); +} + +// ============================================================================= +// With Icons +// ============================================================================= + +@widgetbook.UseCase( + name: 'With Icons', + type: StreamButton, + path: '[Components]/Button', +) +Widget buildStreamButtonWithIcons(BuildContext context) { + final theme = StreamTheme.of(context); + final colorScheme = theme.colorScheme; + final textTheme = theme.textTheme; + + final size = context.knobs.object.dropdown( + label: 'Size', + options: StreamButtonSize.values, + initialOption: StreamButtonSize.large, + labelBuilder: (option) => option.name, + description: 'Button size preset (affects padding and font size).', + ); + + return Center( + child: Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: colorScheme.backgroundSurface, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 80, + child: Text( + 'Leading', + style: textTheme.captionDefault.copyWith( + color: colorScheme.textSecondary, + ), + ), + ), + StreamButton( + label: 'Add Item', + iconLeft: Icons.add, + size: size, + onTap: () {}, + ), + ], + ), + const SizedBox(height: 16), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 80, + child: Text( + 'Trailing', + style: textTheme.captionDefault.copyWith( + color: colorScheme.textSecondary, + ), + ), + ), + StreamButton( + label: 'Continue', + iconRight: Icons.arrow_forward, + size: size, + onTap: () {}, + ), + ], + ), + const SizedBox(height: 16), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 80, + child: Text( + 'Both', + style: textTheme.captionDefault.copyWith( + color: colorScheme.textSecondary, + ), + ), + ), + StreamButton( + label: 'Upload', + iconLeft: Icons.cloud_upload, + iconRight: Icons.arrow_forward, + size: size, + onTap: () {}, + ), + ], + ), + const SizedBox(height: 16), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 80, + child: Text( + 'Icon only', + style: textTheme.captionDefault.copyWith( + color: colorScheme.textSecondary, + ), + ), + ), + StreamButton.icon( + icon: Icons.add, + size: size, + onTap: () {}, + ), + ], + ), + ], + ), + ), + ); +} + +// ============================================================================= +// Real-world Example +// ============================================================================= + +@widgetbook.UseCase( + name: 'Real-world Example', + type: StreamButton, + path: '[Components]/Button', +) +Widget buildStreamButtonExample(BuildContext context) { + final theme = StreamTheme.of(context); + final colorScheme = theme.colorScheme; + final textTheme = theme.textTheme; + + return Center( + child: Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: colorScheme.backgroundSurface, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Common Patterns', + style: textTheme.headingSm.copyWith( + color: colorScheme.textPrimary, + ), + ), + const SizedBox(height: 16), + // Dialog actions + Container( + padding: const EdgeInsets.all(16), + width: 280, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: colorScheme.backgroundApp, + borderRadius: BorderRadius.circular(8), + ), + foregroundDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: colorScheme.borderSurfaceSubtle), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Delete conversation?', + style: textTheme.bodyEmphasis.copyWith( + color: colorScheme.textPrimary, + ), + ), + const SizedBox(height: 4), + Text( + 'This action cannot be undone.', + style: textTheme.captionDefault.copyWith( + color: colorScheme.textSecondary, + ), + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + StreamButton( + label: 'Cancel', + style: StreamButtonStyle.secondary, + size: StreamButtonSize.small, + onTap: () {}, + ), + const SizedBox(width: 8), + StreamButton( + label: 'Delete', + style: StreamButtonStyle.destructive, + size: StreamButtonSize.small, + onTap: () {}, + ), + ], + ), + ], + ), + ), + const SizedBox(height: 12), + // Form submit + Container( + padding: const EdgeInsets.all(16), + width: 280, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: colorScheme.backgroundApp, + borderRadius: BorderRadius.circular(8), + ), + foregroundDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: colorScheme.borderSurfaceSubtle), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Ready to send?', + style: textTheme.bodyEmphasis.copyWith( + color: colorScheme.textPrimary, + ), + ), + const SizedBox(height: 12), + StreamButton( + label: 'Send Message', + iconRight: Icons.send, + onTap: () {}, + ), + ], + ), + ), + ], + ), + ), + ); +} diff --git a/apps/design_system_gallery/lib/config/theme_configuration.dart b/apps/design_system_gallery/lib/config/theme_configuration.dart index a2564b8..75372ad 100644 --- a/apps/design_system_gallery/lib/config/theme_configuration.dart +++ b/apps/design_system_gallery/lib/config/theme_configuration.dart @@ -336,7 +336,7 @@ class ThemeConfiguration extends ChangeNotifier { // Brand brand: _brandPrimaryColor == null ? null - : StreamColorSwatch.fromColor(_brandPrimaryColor ?? StreamColors.blue.shade500), + : StreamColorSwatch.fromColor(_brandPrimaryColor ?? StreamColors.blue.shade500, brightness: _brightness), // Accent - brand primary affects accentPrimary accentPrimary: effectiveAccentPrimary, accentSuccess: _accentSuccess, diff --git a/apps/design_system_gallery/lib/primitives/colors.dart b/apps/design_system_gallery/lib/primitives/colors.dart index 3458394..1b1d34f 100644 --- a/apps/design_system_gallery/lib/primitives/colors.dart +++ b/apps/design_system_gallery/lib/primitives/colors.dart @@ -40,7 +40,7 @@ class _ColorSwatchesList extends StatelessWidget { Widget build(BuildContext context) { final spacing = context.streamSpacing; - const swatches = [ + final swatches = [ _SwatchData( name: 'blue', swatch: StreamColors.blue, @@ -106,7 +106,7 @@ class _FullWidthSwatchCard extends StatelessWidget { final boxShadow = context.streamBoxShadow; final radius = context.streamRadius; final spacing = context.streamSpacing; - final shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]; + final shades = [50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900]; return Padding( padding: EdgeInsets.only(bottom: spacing.md), diff --git a/packages/stream_core_flutter/lib/src/components.dart b/packages/stream_core_flutter/lib/src/components.dart index d4a4b13..c99f49a 100644 --- a/packages/stream_core_flutter/lib/src/components.dart +++ b/packages/stream_core_flutter/lib/src/components.dart @@ -1,4 +1,4 @@ export 'components/avatar/stream_avatar.dart'; export 'components/avatar/stream_avatar_stack.dart'; +export 'components/buttons/stream_button.dart' hide DefaultStreamButton; export 'components/indicator/stream_online_indicator.dart'; -export 'factory/components/stream_button.dart' hide DefaultStreamButton; diff --git a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart new file mode 100644 index 0000000..d5874fb --- /dev/null +++ b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart @@ -0,0 +1,196 @@ +import 'package:flutter/material.dart'; + +import '../../../stream_core_flutter.dart'; +import '../../factory/stream_component_factory.dart' show StreamComponentBuilder; +import '../../theme/components/stream_button_theme.dart'; + +class StreamButton extends StatelessWidget { + StreamButton({ + super.key, + required String label, + VoidCallback? onTap, + StreamButtonStyle style = StreamButtonStyle.primary, + StreamButtonType type = StreamButtonType.solid, + StreamButtonSize size = StreamButtonSize.medium, + IconData? iconLeft, + IconData? iconRight, + }) : props = StreamButtonProps( + label: label, + onTap: onTap, + style: style, + type: type, + size: size, + iconLeft: iconLeft, + iconRight: iconRight, + ); + + StreamButton.icon({ + super.key, + VoidCallback? onTap, + StreamButtonStyle style = StreamButtonStyle.primary, + StreamButtonType type = StreamButtonType.solid, + StreamButtonSize size = StreamButtonSize.medium, + IconData? icon, + }) : props = StreamButtonProps( + label: null, + onTap: onTap, + style: style, + type: type, + size: size, + iconLeft: icon, + iconRight: null, + ); + + final StreamButtonProps props; + + @override + Widget build(BuildContext context) { + return StreamTheme.of( + context, + ).componentFactory.buttonFactory(context, props); + } +} + +class StreamButtonProps { + const StreamButtonProps({ + required this.label, + required this.onTap, + required this.style, + required this.type, + required this.size, + required this.iconLeft, + required this.iconRight, + }); + + final String? label; + final VoidCallback? onTap; + final StreamButtonStyle style; + final StreamButtonType type; + final StreamButtonSize size; + final IconData? iconLeft; + final IconData? iconRight; +} + +enum StreamButtonStyle { primary, secondary, destructive } + +enum StreamButtonSize { small, medium, large } + +enum StreamButtonType { solid, outline, ghost } + +class DefaultStreamButton extends StatelessWidget { + const DefaultStreamButton({super.key, required this.props}); + + static StreamComponentBuilder get factory => + (context, props) => DefaultStreamButton(props: props); + + final StreamButtonProps props; + + @override + Widget build(BuildContext context) { + final spacing = context.streamSpacing; + final buttonTheme = context.streamButtonTheme; + final defaults = _StreamButtonDefaults(context: context); + + final themeColors = switch (props.style) { + StreamButtonStyle.primary => buttonTheme.primaryButtonColors, + StreamButtonStyle.secondary => buttonTheme.secondaryButtonColors, + StreamButtonStyle.destructive => buttonTheme.destructiveButtonColors, + }; + + final defaultColors = switch (props.style) { + StreamButtonStyle.primary => defaults.primaryColors, + StreamButtonStyle.secondary => defaults.secondaryColors, + StreamButtonStyle.destructive => defaults.destructiveColors, + }; + + final backgroundColor = switch (props.type) { + StreamButtonType.solid => themeColors?.solidBackgroundColor ?? defaultColors.solidBackgroundColor, + StreamButtonType.outline => Colors.transparent, + StreamButtonType.ghost => Colors.transparent, + }; + + final foregroundColor = switch (props.type) { + StreamButtonType.solid => themeColors?.solidForegroundColor ?? defaultColors.solidForegroundColor, + StreamButtonType.outline => themeColors?.outlineForegroundColor ?? defaultColors.outlineForegroundColor, + StreamButtonType.ghost => themeColors?.ghostForegroundColor ?? defaultColors.ghostForegroundColor, + }; + + final borderColor = switch (props.type) { + StreamButtonType.solid => null, + StreamButtonType.outline => themeColors?.outlineBorderColor ?? defaultColors.outlineBorderColor, + StreamButtonType.ghost => null, + }; + + final minimumSize = switch (props.size) { + StreamButtonSize.small => 32.0, + StreamButtonSize.medium => 40.0, + StreamButtonSize.large => 48.0, + }; + + const iconSize = 20.0; + + return ElevatedButton( + onPressed: props.onTap, + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all(backgroundColor), + foregroundColor: WidgetStateProperty.all(foregroundColor), + minimumSize: WidgetStateProperty.all(Size(minimumSize, minimumSize)), + padding: WidgetStateProperty.all(EdgeInsets.symmetric(horizontal: spacing.md)), + side: borderColor == null + ? null + : WidgetStateProperty.all( + BorderSide(color: borderColor), + ), + elevation: WidgetStateProperty.all(0), + shape: props.label == null + ? WidgetStateProperty.all(const CircleBorder()) + : WidgetStateProperty.all( + RoundedRectangleBorder(borderRadius: BorderRadius.all(context.streamRadius.max)), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + spacing: spacing.xs, + children: [ + if (props.iconLeft case final iconLeft?) Icon(iconLeft, size: iconSize), + if (props.label case final label?) Text(label), + if (props.iconRight case final iconRight?) Icon(iconRight, size: iconSize), + ], + ), + ); + } +} + +class _StreamButtonDefaults { + _StreamButtonDefaults({ + required this.context, + }) : _colorScheme = context.streamColorScheme; + + final BuildContext context; + final StreamColorScheme _colorScheme; + + StreamButtonColors get primaryColors => StreamButtonColors( + solidBackgroundColor: _colorScheme.brand.shade500, + solidForegroundColor: _colorScheme.textOnAccent, + outlineBorderColor: _colorScheme.brand.shade200, + outlineForegroundColor: _colorScheme.brand.shade500, + ghostForegroundColor: _colorScheme.brand.shade500, + ); + + StreamButtonColors get secondaryColors => StreamButtonColors( + solidBackgroundColor: _colorScheme.backgroundSurface, + solidForegroundColor: _colorScheme.textPrimary, + outlineBorderColor: _colorScheme.borderDefault, + outlineForegroundColor: _colorScheme.textPrimary, + ghostForegroundColor: _colorScheme.textPrimary, + ); + + StreamButtonColors get destructiveColors => StreamButtonColors( + solidBackgroundColor: _colorScheme.accentError, + solidForegroundColor: _colorScheme.textOnAccent, + outlineBorderColor: _colorScheme.accentError, + outlineForegroundColor: _colorScheme.textOnAccent, + ghostForegroundColor: _colorScheme.textOnAccent, + ); +} diff --git a/packages/stream_core_flutter/lib/src/factory/components.dart b/packages/stream_core_flutter/lib/src/factory/components.dart deleted file mode 100644 index acfdf33..0000000 --- a/packages/stream_core_flutter/lib/src/factory/components.dart +++ /dev/null @@ -1 +0,0 @@ -export 'components/stream_button_theme.dart'; diff --git a/packages/stream_core_flutter/lib/src/factory/components/stream_button.dart b/packages/stream_core_flutter/lib/src/factory/components/stream_button.dart deleted file mode 100644 index bb1f0b5..0000000 --- a/packages/stream_core_flutter/lib/src/factory/components/stream_button.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../stream_component_factory.dart' show StreamComponentBuilder; -import '../stream_theme.dart'; -import 'stream_button_theme.dart'; - -class StreamButton extends StatelessWidget { - StreamButton({ - super.key, - String? label, - VoidCallback? onTap, - StreamButtonType type = StreamButtonType.primary, - StreamButtonSize size = StreamButtonSize.medium, - Widget? iconLeft, - Widget? iconRight, - }) : props = StreamButtonProps( - label: label, - onTap: onTap, - type: type, - size: size, - iconLeft: iconLeft, - iconRight: iconRight, - ); - - final StreamButtonProps props; - - @override - Widget build(BuildContext context) { - return StreamTheme.of( - context, - ).componentFactory.buttonFactory(context, props); - } -} - -class StreamButtonProps { - const StreamButtonProps({ - required this.label, - required this.onTap, - required this.type, - required this.size, - required this.iconLeft, - required this.iconRight, - }); - - final String? label; - final VoidCallback? onTap; - final StreamButtonType type; - final StreamButtonSize size; - final Widget? iconLeft; - final Widget? iconRight; -} - -enum StreamButtonType { primary, secondary, destructive } - -enum StreamButtonSize { small, medium, large } - -class DefaultStreamButton extends StatelessWidget { - const DefaultStreamButton({super.key, required this.props}); - - static StreamComponentBuilder get factory => - (context, props) => DefaultStreamButton(props: props); - - final StreamButtonProps props; - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final streamTheme = StreamTheme.of(context); - final buttonTheme = streamTheme.buttonTheme; - - final colors = switch (props.type) { - StreamButtonType.primary => _primaryColors( - theme, - streamTheme, - buttonTheme, - ), - StreamButtonType.secondary => _secondaryColors( - theme, - streamTheme, - buttonTheme, - ), - StreamButtonType.destructive => _destructiveColors( - theme, - streamTheme, - buttonTheme, - ), - }; - - return ElevatedButton( - onPressed: props.onTap, - style: ButtonStyle( - backgroundColor: colors.bgColor, - foregroundColor: colors.textColor, - side: WidgetStateProperty.resolveWith( - (states) => BorderSide( - color: colors.borderColor.resolve(states), - ), - ), - elevation: WidgetStateProperty.all(0), - ), - child: Row( - children: [ - ?props.iconLeft, - if (props.label case final label?) Text(label), - ?props.iconRight, - ], - ), - ); - } - - _StreamButtonColors _primaryColors( - ThemeData theme, - StreamTheme streamTheme, - StreamButtonTheme buttonTheme, - ) => _StreamButtonColors( - bgColor: - buttonTheme.primaryColor ?? - (streamTheme.primaryColor != null - ? WidgetStateProperty.all(streamTheme.primaryColor!) - : WidgetStateProperty.all(theme.colorScheme.primary)), - borderColor: - buttonTheme.primaryColor ?? - (streamTheme.primaryColor != null - ? WidgetStateProperty.all(streamTheme.primaryColor!) - : WidgetStateProperty.all(theme.colorScheme.primary)), - textColor: WidgetStateProperty.all(Colors.white), - ); - - _StreamButtonColors _secondaryColors( - ThemeData theme, - StreamTheme streamTheme, - StreamButtonTheme buttonTheme, - ) => _StreamButtonColors( - bgColor: WidgetStateProperty.all(Colors.white), - borderColor: WidgetStateProperty.all(Colors.grey), - textColor: WidgetStateProperty.all(Colors.black), - ); - - _StreamButtonColors _destructiveColors( - ThemeData theme, - StreamTheme streamTheme, - StreamButtonTheme buttonTheme, - ) => _StreamButtonColors( - bgColor: WidgetStateProperty.all(Colors.red), - borderColor: WidgetStateProperty.all(Colors.red), - textColor: WidgetStateProperty.all(Colors.white), - ); -} - -class _StreamButtonColors { - _StreamButtonColors({ - required this.bgColor, - required this.borderColor, - required this.textColor, - }); - - final WidgetStateProperty bgColor; - final WidgetStateProperty borderColor; - final WidgetStateProperty textColor; -} diff --git a/packages/stream_core_flutter/lib/src/factory/components/stream_button_theme.dart b/packages/stream_core_flutter/lib/src/factory/components/stream_button_theme.dart deleted file mode 100644 index 9455795..0000000 --- a/packages/stream_core_flutter/lib/src/factory/components/stream_button_theme.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/widgets.dart'; - -import '../stream_theme.dart'; - -class StreamButtonTheme { - StreamButtonTheme({this.primaryColor}); - - final WidgetStateProperty? primaryColor; - - static StreamButtonTheme of(BuildContext context) { - return StreamTheme.of(context).buttonTheme; - } -} 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 39d6289..17a08de 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 @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'components/stream_button.dart' show DefaultStreamButton, StreamButtonProps; +import '../components/buttons/stream_button.dart' show DefaultStreamButton, StreamButtonProps; typedef StreamComponentBuilder = Widget Function(BuildContext context, T props); diff --git a/packages/stream_core_flutter/lib/src/factory/stream_theme.dart b/packages/stream_core_flutter/lib/src/factory/stream_theme.dart deleted file mode 100644 index 8c1e77f..0000000 --- a/packages/stream_core_flutter/lib/src/factory/stream_theme.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'components/stream_button_theme.dart'; -import 'stream_component_factory.dart'; - -class StreamTheme extends ThemeExtension { - StreamTheme({ - StreamComponentFactory? componentFactory, - this.primaryColor, - StreamButtonTheme? buttonTheme, - }) : componentFactory = componentFactory ?? StreamComponentFactory(), - buttonTheme = buttonTheme ?? StreamButtonTheme(); - - final StreamComponentFactory componentFactory; - - final Color? primaryColor; - - final StreamButtonTheme buttonTheme; - - static StreamTheme of(BuildContext context) { - return Theme.of(context).extension() ?? StreamTheme(); - } - - @override - StreamTheme copyWith({Color? primaryColor, StreamButtonTheme? buttonTheme}) { - return StreamTheme( - componentFactory: componentFactory, - primaryColor: primaryColor ?? this.primaryColor, - buttonTheme: buttonTheme ?? this.buttonTheme, - ); - } - - @override - ThemeExtension lerp( - covariant ThemeExtension? other, - double t, - ) { - if (other is! StreamTheme) { - return this; - } - return StreamTheme(); - } -} diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart new file mode 100644 index 0000000..5098a03 --- /dev/null +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart @@ -0,0 +1,61 @@ +import 'package:flutter/widgets.dart'; +import 'package:theme_extensions_builder_annotation/theme_extensions_builder_annotation.dart'; + +import '../stream_theme.dart'; + +part 'stream_button_theme.g.theme.dart'; + +class StreamButtonTheme extends InheritedTheme { + const StreamButtonTheme({ + super.key, + required this.data, + required super.child, + }); + + final StreamButtonThemeData data; + + static StreamButtonThemeData of(BuildContext context) { + final localTheme = context.dependOnInheritedWidgetOfExactType(); + return StreamTheme.of(context).buttonTheme.merge(localTheme?.data); + } + + @override + Widget wrap(BuildContext context, Widget child) { + return StreamButtonTheme(data: data, child: child); + } + + @override + bool updateShouldNotify(StreamButtonTheme oldWidget) => data != oldWidget.data; +} + +@themeGen +@immutable +class StreamButtonThemeData with _$StreamButtonThemeData { + const StreamButtonThemeData({ + this.primaryButtonColors, + this.secondaryButtonColors, + this.destructiveButtonColors, + }); + + final StreamButtonColors? primaryButtonColors; + final StreamButtonColors? secondaryButtonColors; + final StreamButtonColors? destructiveButtonColors; +} + +@themeGen +@immutable +class StreamButtonColors with _$StreamButtonColors { + const StreamButtonColors({ + this.solidBackgroundColor, + this.solidForegroundColor, + this.outlineBorderColor, + this.outlineForegroundColor, + this.ghostForegroundColor, + }); + + final Color? solidBackgroundColor; + final Color? solidForegroundColor; + final Color? outlineBorderColor; + final Color? outlineForegroundColor; + final Color? ghostForegroundColor; +} diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart new file mode 100644 index 0000000..b79fa83 --- /dev/null +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart @@ -0,0 +1,237 @@ +// dart format width=80 +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, unused_element + +part of 'stream_button_theme.dart'; + +// ************************************************************************** +// ThemeGenGenerator +// ************************************************************************** + +mixin _$StreamButtonThemeData { + bool get canMerge => true; + + static StreamButtonThemeData? lerp( + StreamButtonThemeData? a, + StreamButtonThemeData? 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 StreamButtonThemeData( + primaryButtonColors: t < 0.5 + ? a.primaryButtonColors + : b.primaryButtonColors, + secondaryButtonColors: t < 0.5 + ? a.secondaryButtonColors + : b.secondaryButtonColors, + destructiveButtonColors: t < 0.5 + ? a.destructiveButtonColors + : b.destructiveButtonColors, + ); + } + + StreamButtonThemeData copyWith({ + StreamButtonColors? primaryButtonColors, + StreamButtonColors? secondaryButtonColors, + StreamButtonColors? destructiveButtonColors, + }) { + final _this = (this as StreamButtonThemeData); + + return StreamButtonThemeData( + primaryButtonColors: primaryButtonColors ?? _this.primaryButtonColors, + secondaryButtonColors: + secondaryButtonColors ?? _this.secondaryButtonColors, + destructiveButtonColors: + destructiveButtonColors ?? _this.destructiveButtonColors, + ); + } + + StreamButtonThemeData merge(StreamButtonThemeData? other) { + final _this = (this as StreamButtonThemeData); + + if (other == null || identical(_this, other)) { + return _this; + } + + if (!other.canMerge) { + return other; + } + + return copyWith( + primaryButtonColors: + _this.primaryButtonColors?.merge(other.primaryButtonColors) ?? + other.primaryButtonColors, + secondaryButtonColors: + _this.secondaryButtonColors?.merge(other.secondaryButtonColors) ?? + other.secondaryButtonColors, + destructiveButtonColors: + _this.destructiveButtonColors?.merge(other.destructiveButtonColors) ?? + other.destructiveButtonColors, + ); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + + if (other.runtimeType != runtimeType) { + return false; + } + + final _this = (this as StreamButtonThemeData); + final _other = (other as StreamButtonThemeData); + + return _other.primaryButtonColors == _this.primaryButtonColors && + _other.secondaryButtonColors == _this.secondaryButtonColors && + _other.destructiveButtonColors == _this.destructiveButtonColors; + } + + @override + int get hashCode { + final _this = (this as StreamButtonThemeData); + + return Object.hash( + runtimeType, + _this.primaryButtonColors, + _this.secondaryButtonColors, + _this.destructiveButtonColors, + ); + } +} + +mixin _$StreamButtonColors { + bool get canMerge => true; + + static StreamButtonColors? lerp( + StreamButtonColors? a, + StreamButtonColors? 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 StreamButtonColors( + solidBackgroundColor: Color.lerp( + a.solidBackgroundColor, + b.solidBackgroundColor, + t, + ), + solidForegroundColor: Color.lerp( + a.solidForegroundColor, + b.solidForegroundColor, + t, + ), + outlineBorderColor: Color.lerp( + a.outlineBorderColor, + b.outlineBorderColor, + t, + ), + outlineForegroundColor: Color.lerp( + a.outlineForegroundColor, + b.outlineForegroundColor, + t, + ), + ghostForegroundColor: Color.lerp( + a.ghostForegroundColor, + b.ghostForegroundColor, + t, + ), + ); + } + + StreamButtonColors copyWith({ + Color? solidBackgroundColor, + Color? solidForegroundColor, + Color? outlineBorderColor, + Color? outlineForegroundColor, + Color? ghostForegroundColor, + }) { + final _this = (this as StreamButtonColors); + + return StreamButtonColors( + solidBackgroundColor: solidBackgroundColor ?? _this.solidBackgroundColor, + solidForegroundColor: solidForegroundColor ?? _this.solidForegroundColor, + outlineBorderColor: outlineBorderColor ?? _this.outlineBorderColor, + outlineForegroundColor: + outlineForegroundColor ?? _this.outlineForegroundColor, + ghostForegroundColor: ghostForegroundColor ?? _this.ghostForegroundColor, + ); + } + + StreamButtonColors merge(StreamButtonColors? other) { + final _this = (this as StreamButtonColors); + + if (other == null || identical(_this, other)) { + return _this; + } + + if (!other.canMerge) { + return other; + } + + return copyWith( + solidBackgroundColor: other.solidBackgroundColor, + solidForegroundColor: other.solidForegroundColor, + outlineBorderColor: other.outlineBorderColor, + outlineForegroundColor: other.outlineForegroundColor, + ghostForegroundColor: other.ghostForegroundColor, + ); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + + if (other.runtimeType != runtimeType) { + return false; + } + + final _this = (this as StreamButtonColors); + final _other = (other as StreamButtonColors); + + return _other.solidBackgroundColor == _this.solidBackgroundColor && + _other.solidForegroundColor == _this.solidForegroundColor && + _other.outlineBorderColor == _this.outlineBorderColor && + _other.outlineForegroundColor == _this.outlineForegroundColor && + _other.ghostForegroundColor == _this.ghostForegroundColor; + } + + @override + int get hashCode { + final _this = (this as StreamButtonColors); + + return Object.hash( + runtimeType, + _this.solidBackgroundColor, + _this.solidForegroundColor, + _this.outlineBorderColor, + _this.outlineForegroundColor, + _this.ghostForegroundColor, + ); + } +} diff --git a/packages/stream_core_flutter/lib/src/theme/primitives/stream_color_swatch_helper.dart b/packages/stream_core_flutter/lib/src/theme/primitives/stream_color_swatch_helper.dart index 9dc5fda..d62a6c4 100644 --- a/packages/stream_core_flutter/lib/src/theme/primitives/stream_color_swatch_helper.dart +++ b/packages/stream_core_flutter/lib/src/theme/primitives/stream_color_swatch_helper.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; class StreamColorSwatchHelper { @@ -8,53 +9,70 @@ class StreamColorSwatchHelper { /// This internal method creates the shade variations using HSL color space /// for more natural color transitions. The center shade (500) uses the original /// color's lightness, and other shades are calculated proportionally. - static Map generateShadeMap(Color baseColor) { + /// + /// When [brightness] is [Brightness.dark], the lightness values are inverted + /// so that lower shade numbers (e.g., 50) are darker and higher shade numbers + /// (e.g., 900) are lighter. + static Map generateShadeMap( + Color baseColor, { + Brightness brightness = Brightness.light, + }) { final hslBase = HSLColor.fromColor(baseColor); final centerLightness = hslBase.lightness; + final shades = [50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900]; + return { - 50: _adjustLightness(hslBase, _calculateLightness(50, centerLightness)), - 100: _adjustLightness(hslBase, _calculateLightness(100, centerLightness)), - 150: _adjustLightness(hslBase, _calculateLightness(150, centerLightness)), - 200: _adjustLightness(hslBase, _calculateLightness(200, centerLightness)), - 300: _adjustLightness(hslBase, _calculateLightness(300, centerLightness)), - 400: _adjustLightness(hslBase, _calculateLightness(400, centerLightness)), - 501: _adjustLightness(hslBase, _calculateLightness(500, centerLightness)), - 600: _adjustLightness(hslBase, _calculateLightness(600, centerLightness)), - 700: _adjustLightness(hslBase, _calculateLightness(700, centerLightness)), - 800: _adjustLightness(hslBase, _calculateLightness(800, centerLightness)), - 900: _adjustLightness(hslBase, _calculateLightness(900, centerLightness)), + for (final shade in shades) + shade: _adjustLightness( + hslBase, + _calculateLightness(shade, centerLightness, brightness: brightness), + ), }; } /// Calculates the target lightness for a given shade number based on the center lightness. /// /// The formula maps shade numbers (50-900) to lightness values where: - /// - 50 is the lightest (0.95) - /// - 500 uses the center lightness (original color's lightness) - /// - 900 is the darkest (0.12) + /// - For [Brightness.light]: + /// - 50 is the lightest (0.95) + /// - 500 uses the center lightness (original color's lightness) + /// - 900 is the darkest (0.12) + /// - For [Brightness.dark]: + /// - 50 is the darkest (0.12) + /// - 500 uses the center lightness (original color's lightness) + /// - 900 is the lightest (0.95) /// - /// For shades lighter than 500: lightness increases proportionally from center to 0.95 - /// For shades darker than 500: lightness decreases proportionally from center to 0.12 - static double _calculateLightness(int shade, double centerLightness) { - const minLightness = 0.12; // Darkest shade (900) - const maxLightness = 0.95; // Lightest shade (50) + /// For shades lighter than 500: lightness increases proportionally from center to 0.95 (light mode) + /// For shades darker than 500: lightness decreases proportionally from center to 0.12 (light mode) + /// In dark mode, these mappings are inverted. + static double _calculateLightness( + int shade, + double centerLightness, { + Brightness brightness = Brightness.light, + }) { + const minLightness = 0.12; // Darkest shade + const maxLightness = 0.95; // Lightest shade const centerShade = 500; if (shade == centerShade) { return centerLightness; } + final isDark = brightness == Brightness.dark; + if (shade < centerShade) { - // Lighter shades: interpolate between centerLightness and maxLightness - // Map shade from [50, 500) to lightness [maxLightness, centerLightness) + // For light mode: lighter shades (toward maxLightness) + // For dark mode: darker shades (toward minLightness) + final targetLightness = isDark ? minLightness : maxLightness; final t = (shade - 50) / (centerShade - 50); - return maxLightness - (maxLightness - centerLightness) * t; + return targetLightness - (targetLightness - centerLightness) * t; } else { - // Darker shades: interpolate between centerLightness and minLightness - // Map shade from (500, 900] to lightness (centerLightness, minLightness] + // For light mode: darker shades (toward minLightness) + // For dark mode: lighter shades (toward maxLightness) + final targetLightness = isDark ? maxLightness : minLightness; final t = (shade - centerShade) / (900 - centerShade); - return centerLightness - (centerLightness - minLightness) * t; + return centerLightness - (centerLightness - targetLightness) * t; } } diff --git a/packages/stream_core_flutter/lib/src/theme/primitives/stream_colors.dart b/packages/stream_core_flutter/lib/src/theme/primitives/stream_colors.dart index b0d9344..45eadc5 100644 --- a/packages/stream_core_flutter/lib/src/theme/primitives/stream_colors.dart +++ b/packages/stream_core_flutter/lib/src/theme/primitives/stream_colors.dart @@ -2,13 +2,13 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; import 'stream_color_swatch_helper.dart'; +import 'tokens/light/stream_tokens.dart' as tokens; /// [Color] and [ColorSwatch] constants for the Stream design system. /// -/// Most swatches have colors from 50 to 950. The smaller the number, the more +/// Most swatches have colors from 50 to 900. The smaller the number, the more /// pale the color. The greater the number, the darker the color. Shades 50-450 -/// are incremented by 50, and shades 500-950 are incremented by 100 or 50 for -/// the darkest shade (950). +/// are incremented by 50, and shades 500-900 are incremented by 100. /// /// In addition, a series of blacks and whites with common opacities are /// available. For example, [white70] is a pure white with 70% opacity. @@ -37,186 +37,224 @@ abstract final class StreamColors { const StreamColors._(); /// The fully transparent color. - static const transparent = Color(0x00000000); + static const transparent = tokens.StreamTokens.baseTransparent0; /// The pure white color. - static const white = Color(0xFFFFFFFF); + static const white = tokens.StreamTokens.baseWhite; /// The white color with 10% opacity. - static const white10 = Color(0x1AFFFFFF); + static const white10 = tokens.StreamTokens.baseTransparentWhite10; /// The white color with 20% opacity. - static const white20 = Color(0x33FFFFFF); + static const white20 = tokens.StreamTokens.baseTransparentWhite20; /// The white color with 50% opacity. static const white50 = Color(0x80FFFFFF); /// The white color with 70% opacity. - static const white70 = Color(0xB3FFFFFF); + static const white70 = tokens.StreamTokens.baseTransparentWhite70; /// The pure black color. - static const black = Color(0xFF000000); + static const black = tokens.StreamTokens.baseBlack; /// The black color with 5% opacity. - static const black5 = Color(0x0D000000); + static const black5 = tokens.StreamTokens.baseTransparentBlack5; /// The black color with 10% opacity. - static const black10 = Color(0x1A000000); + static const black10 = tokens.StreamTokens.baseTransparentBlack10; /// The black color with 50% opacity. static const black50 = Color(0x80000000); /// The slate color swatch. - static const slate = StreamColorSwatch( - _slatePrimaryValue, - { - 50: Color(0xFFFAFBFC), - 100: Color(0xFFF2F4F6), - 200: Color(0xFFE2E6EA), - 300: Color(0xFFD0D5DA), - 400: Color(0xFFB8BEC4), - 500: Color(_slatePrimaryValue), - 600: Color(0xFF838990), - 700: Color(0xFF6A7077), - 800: Color(0xFF50565D), - 900: Color(0xFF384047), - 950: Color(0xFF1E252B), + static final slate = StreamColorSwatch( + _slatePrimaryValue.toARGB32(), + const { + 50: tokens.StreamTokens.slate50, + 100: tokens.StreamTokens.slate100, + 150: tokens.StreamTokens.slate150, + 200: tokens.StreamTokens.slate200, + 300: tokens.StreamTokens.slate300, + 400: tokens.StreamTokens.slate400, + 500: tokens.StreamTokens.slate500, + 600: tokens.StreamTokens.slate600, + 700: tokens.StreamTokens.slate700, + 800: tokens.StreamTokens.slate800, + 900: tokens.StreamTokens.slate900, }, ); - static const _slatePrimaryValue = 0xFF9EA4AA; + static const _slatePrimaryValue = tokens.StreamTokens.slate500; /// The neutral color swatch. - static const neutral = StreamColorSwatch( - _neutralPrimaryValue, - { - 50: Color(0xFFF7F7F7), - 100: Color(0xFFEDEDED), - 200: Color(0xFFD9D9D9), - 300: Color(0xFFC1C1C1), - 400: Color(0xFFA3A3A3), - 500: Color(_neutralPrimaryValue), - 600: Color(0xFF636363), - 700: Color(0xFF4A4A4A), - 800: Color(0xFF383838), - 900: Color(0xFF262626), - 950: Color(0xFF151515), + static final neutral = StreamColorSwatch( + _neutralPrimaryValue.toARGB32(), + const { + 50: tokens.StreamTokens.neutral50, + 100: tokens.StreamTokens.neutral100, + 150: tokens.StreamTokens.neutral150, + 200: tokens.StreamTokens.neutral200, + 300: tokens.StreamTokens.neutral300, + 400: tokens.StreamTokens.neutral400, + 500: tokens.StreamTokens.neutral500, + 600: tokens.StreamTokens.neutral600, + 700: tokens.StreamTokens.neutral700, + 800: tokens.StreamTokens.neutral800, + 900: tokens.StreamTokens.neutral900, }, ); - static const _neutralPrimaryValue = 0xFF7F7F7F; + static const _neutralPrimaryValue = tokens.StreamTokens.neutral500; /// The blue color swatch. - static const blue = StreamColorSwatch( - _bluePrimaryValue, - { - 50: Color(0xFFEBF3FF), - 100: Color(0xFFD2E3FF), - 200: Color(0xFFA6C4FF), - 300: Color(0xFF7AA7FF), - 400: Color(0xFF4E8BFF), - 500: Color(_bluePrimaryValue), - 600: Color(0xFF0052CE), - 700: Color(0xFF0042A3), - 800: Color(0xFF003179), - 900: Color(0xFF001F4F), - 950: Color(0xFF001025), + static final blue = StreamColorSwatch( + _bluePrimaryValue.toARGB32(), + const { + 50: tokens.StreamTokens.blue50, + 100: tokens.StreamTokens.blue100, + 150: tokens.StreamTokens.blue150, + 200: tokens.StreamTokens.blue200, + 300: tokens.StreamTokens.blue300, + 400: tokens.StreamTokens.blue400, + 500: tokens.StreamTokens.blue500, + 600: tokens.StreamTokens.blue600, + 700: tokens.StreamTokens.blue700, + 800: tokens.StreamTokens.blue800, + 900: tokens.StreamTokens.blue900, }, ); - static const _bluePrimaryValue = 0xFF005FFF; + static const _bluePrimaryValue = tokens.StreamTokens.blue500; /// The cyan color swatch. - static const cyan = StreamColorSwatch( - _cyanPrimaryValue, - { - 50: Color(0xFFF0FCFE), - 100: Color(0xFFD7F7FB), - 200: Color(0xFFBDF1F8), - 300: Color(0xFFA3ECF4), - 400: Color(0xFF89E6F1), - 500: Color(_cyanPrimaryValue), - 600: Color(0xFF3EC9D9), - 700: Color(0xFF28A8B5), - 800: Color(0xFF1C8791), - 900: Color(0xFF125F66), - 950: Color(0xFF0B3D44), + static final cyan = StreamColorSwatch( + _cyanPrimaryValue.toARGB32(), + const { + 50: tokens.StreamTokens.cyan50, + 100: tokens.StreamTokens.cyan100, + 150: tokens.StreamTokens.cyan150, + 200: tokens.StreamTokens.cyan200, + 300: tokens.StreamTokens.cyan300, + 400: tokens.StreamTokens.cyan400, + 500: tokens.StreamTokens.cyan500, + 600: tokens.StreamTokens.cyan600, + 700: tokens.StreamTokens.cyan700, + 800: tokens.StreamTokens.cyan800, + 900: tokens.StreamTokens.cyan900, }, ); - static const _cyanPrimaryValue = 0xFF69E5F6; + static const _cyanPrimaryValue = tokens.StreamTokens.cyan500; /// The green color swatch. - static const green = StreamColorSwatch( - _greenPrimaryValue, - { - 50: Color(0xFFE8FFF5), - 100: Color(0xFFC9FCE7), - 200: Color(0xFFA9F8D9), - 300: Color(0xFF88F2CA), - 400: Color(0xFF59E9B5), - 500: Color(_greenPrimaryValue), - 600: Color(0xFF00B681), - 700: Color(0xFF008D64), - 800: Color(0xFF006548), - 900: Color(0xFF003D2B), - 950: Color(0xFF002319), + static final green = StreamColorSwatch( + _greenPrimaryValue.toARGB32(), + const { + 50: tokens.StreamTokens.green50, + 100: tokens.StreamTokens.green100, + 150: tokens.StreamTokens.green150, + 200: tokens.StreamTokens.green200, + 300: tokens.StreamTokens.green300, + 400: tokens.StreamTokens.green400, + 500: tokens.StreamTokens.green500, + 600: tokens.StreamTokens.green600, + 700: tokens.StreamTokens.green700, + 800: tokens.StreamTokens.green800, + 900: tokens.StreamTokens.green900, }, ); - static const _greenPrimaryValue = 0xFF00E2A1; + static const _greenPrimaryValue = tokens.StreamTokens.green500; /// The purple color swatch. - static const purple = StreamColorSwatch( - _purplePrimaryValue, - { - 50: Color(0xFFF5EFFE), - 100: Color(0xFFEBDEFD), - 200: Color(0xFFD8BFFC), - 300: Color(0xFFC79FFC), - 400: Color(0xFFB98AF9), - 500: Color(_purplePrimaryValue), - 600: Color(0xFF996CE3), - 700: Color(0xFF7F55C7), - 800: Color(0xFF6640AB), - 900: Color(0xFF4D2C8F), - 950: Color(0xFF351C6B), + static final purple = StreamColorSwatch( + _purplePrimaryValue.toARGB32(), + const { + 50: tokens.StreamTokens.purple50, + 100: tokens.StreamTokens.purple100, + 150: tokens.StreamTokens.purple150, + 200: tokens.StreamTokens.purple200, + 300: tokens.StreamTokens.purple300, + 400: tokens.StreamTokens.purple400, + 500: tokens.StreamTokens.purple500, + 600: tokens.StreamTokens.purple600, + 700: tokens.StreamTokens.purple700, + 800: tokens.StreamTokens.purple800, + 900: tokens.StreamTokens.purple900, }, ); - static const _purplePrimaryValue = 0xFFB38AF8; + static const _purplePrimaryValue = tokens.StreamTokens.purple500; /// The yellow color swatch. - static const yellow = StreamColorSwatch( - _yellowPrimaryValue, - { - 50: Color(0xFFFFF9E5), - 100: Color(0xFFFFF1C2), - 200: Color(0xFFFFE8A0), - 300: Color(0xFFFFDE7D), - 400: Color(0xFFFFD65A), - 500: Color(_yellowPrimaryValue), - 600: Color(0xFFE6B400), - 700: Color(0xFFC59600), - 800: Color(0xFF9F7700), - 900: Color(0xFF7A5A00), - 950: Color(0xFF4F3900), + static final yellow = StreamColorSwatch( + _yellowPrimaryValue.toARGB32(), + const { + 50: tokens.StreamTokens.yellow50, + 100: tokens.StreamTokens.yellow100, + 150: tokens.StreamTokens.yellow150, + 200: tokens.StreamTokens.yellow200, + 300: tokens.StreamTokens.yellow300, + 400: tokens.StreamTokens.yellow400, + 500: tokens.StreamTokens.yellow500, + 600: tokens.StreamTokens.yellow600, + 700: tokens.StreamTokens.yellow700, + 800: tokens.StreamTokens.yellow800, + 900: tokens.StreamTokens.yellow900, }, ); - static const _yellowPrimaryValue = 0xFFFFD233; + static const _yellowPrimaryValue = tokens.StreamTokens.yellow500; /// The red color swatch. - static const red = StreamColorSwatch( - _redPrimaryValue, - { - 50: Color(0xFFFCEBEA), - 100: Color(0xFFF8CFCD), - 200: Color(0xFFF3B3B0), - 300: Color(0xFFED958F), - 400: Color(0xFFE6756C), - 500: Color(_redPrimaryValue), - 600: Color(0xFFB9261F), - 700: Color(0xFF98201A), - 800: Color(0xFF761915), - 900: Color(0xFF54120F), - 950: Color(0xFF360B09), + static final red = StreamColorSwatch( + _redPrimaryValue.toARGB32(), + const { + 50: tokens.StreamTokens.red50, + 100: tokens.StreamTokens.red100, + 150: tokens.StreamTokens.red150, + 200: tokens.StreamTokens.red200, + 300: tokens.StreamTokens.red300, + 400: tokens.StreamTokens.red400, + 500: tokens.StreamTokens.red500, + 600: tokens.StreamTokens.red600, + 700: tokens.StreamTokens.red700, + 800: tokens.StreamTokens.red800, + 900: tokens.StreamTokens.red900, }, ); - static const _redPrimaryValue = 0xFFD92F26; + static const _redPrimaryValue = tokens.StreamTokens.red500; + + /// The violet color swatch. + static final violet = StreamColorSwatch( + _violetPrimaryValue.toARGB32(), + const { + 50: tokens.StreamTokens.violet50, + 100: tokens.StreamTokens.violet100, + 150: tokens.StreamTokens.violet150, + 200: tokens.StreamTokens.violet200, + 300: tokens.StreamTokens.violet300, + 400: tokens.StreamTokens.violet400, + 500: tokens.StreamTokens.violet500, + 600: tokens.StreamTokens.violet600, + 700: tokens.StreamTokens.violet700, + 800: tokens.StreamTokens.violet800, + 900: tokens.StreamTokens.violet900, + }, + ); + static const _violetPrimaryValue = tokens.StreamTokens.violet500; + + /// The lime color swatch. + static final lime = StreamColorSwatch( + _limePrimaryValue.toARGB32(), + const { + 50: tokens.StreamTokens.lime50, + 100: tokens.StreamTokens.lime100, + 150: tokens.StreamTokens.lime150, + 200: tokens.StreamTokens.lime200, + 300: tokens.StreamTokens.lime300, + 400: tokens.StreamTokens.lime400, + 500: tokens.StreamTokens.lime500, + 600: tokens.StreamTokens.lime600, + 700: tokens.StreamTokens.lime700, + 800: tokens.StreamTokens.lime800, + 900: tokens.StreamTokens.lime900, + }, + ); + static const _limePrimaryValue = tokens.StreamTokens.lime500; } /// A color swatch with multiple shades of a single color. @@ -228,8 +266,11 @@ abstract final class StreamColors { class StreamColorSwatch extends ColorSwatch { const StreamColorSwatch(super.primary, super._swatch); - factory StreamColorSwatch.fromColor(Color color) { - return StreamColorSwatch(color.toARGB32(), StreamColorSwatchHelper.generateShadeMap(color)); + factory StreamColorSwatch.fromColor(Color color, {Brightness brightness = Brightness.light}) { + return StreamColorSwatch( + color.toARGB32(), + StreamColorSwatchHelper.generateShadeMap(color, brightness: brightness), + ); } /// The lightest shade. @@ -238,6 +279,9 @@ class StreamColorSwatch extends ColorSwatch { /// The second lightest shade. Color get shade100 => this[100]!; + /// The shade between 100 and 200. + Color get shade150 => this[150]!; + /// The third lightest shade. Color get shade200 => this[200]!; @@ -261,7 +305,4 @@ class StreamColorSwatch extends ColorSwatch { /// The second shade. Color get shade900 => this[900]!; - - /// The darkest shade. - Color get shade950 => this[950]!; } diff --git a/packages/stream_core_flutter/lib/src/theme/primitives/tokens/dark/stream_tokens.dart b/packages/stream_core_flutter/lib/src/theme/primitives/tokens/dark/stream_tokens.dart new file mode 100644 index 0000000..b06d579 --- /dev/null +++ b/packages/stream_core_flutter/lib/src/theme/primitives/tokens/dark/stream_tokens.dart @@ -0,0 +1,572 @@ +import 'package:flutter/widgets.dart'; + +class StreamTokens { + StreamTokens._(); + + static const baseTransparent0 = Color(0x00FFFFFF); + static const baseTransparentBlack5 = Color(0x0D000000); + static const baseTransparentBlack10 = Color(0x1A000000); + static const baseTransparentWhite70 = Color(0xB3FFFFFF); + static const baseTransparentWhite10 = Color(0x1AFFFFFF); + static const baseTransparentWhite20 = Color(0x33FFFFFF); + static const baseTransparentBlack70 = Color(0xB3000000); + static const baseBlack = Color(0xFF000000); + static const baseWhite = Color(0xFFFFFFFF); + static const slate50 = Color(0xFFF6F8FA); + static const slate100 = Color(0xFFEBEEF1); + static const slate150 = Color(0xFFD5DBE1); + static const slate200 = Color(0xFFC0C8D2); + static const slate300 = Color(0xFFA3ACBA); + static const slate400 = Color(0xFF87909F); + static const slate500 = Color(0xFF687385); + static const slate600 = Color(0xFF545969); + static const slate700 = Color(0xFF414552); + static const slate800 = Color(0xFF30313D); + static const slate900 = Color(0xFF1A1B25); + static const neutral50 = Color(0xFFF8F8F8); + static const neutral100 = Color(0xFFEFEFEF); + static const neutral150 = Color(0xFFD8D8D8); + static const neutral200 = Color(0xFFC4C4C4); + static const neutral300 = Color(0xFFABABAB); + static const neutral400 = Color(0xFF8F8F8F); + static const neutral500 = Color(0xFF6A6A6A); + static const neutral600 = Color(0xFF565656); + static const neutral700 = Color(0xFF464646); + static const neutral800 = Color(0xFF323232); + static const neutral900 = Color(0xFF1C1C1C); + static const blue50 = Color(0xFFF3F7FF); + static const blue100 = Color(0xFFE3EDFF); + static const blue150 = Color(0xFFC3D9FF); + static const blue200 = Color(0xFFA5C5FF); + static const blue300 = Color(0xFF78A8FF); + static const blue400 = Color(0xFF4586FF); + static const blue500 = Color(0xFF005FFF); + static const blue600 = Color(0xFF1B53BD); + static const blue700 = Color(0xFF19418D); + static const blue800 = Color(0xFF142F63); + static const blue900 = Color(0xFF091A3B); + static const cyan50 = Color(0xFFF1FBFC); + static const cyan100 = Color(0xFFD1F3F6); + static const cyan150 = Color(0xFFA9E4EA); + static const cyan200 = Color(0xFF72D7E0); + static const cyan300 = Color(0xFF45BCC7); + static const cyan400 = Color(0xFF1E9EA9); + static const cyan500 = Color(0xFF248088); + static const cyan600 = Color(0xFF006970); + static const cyan700 = Color(0xFF065056); + static const cyan800 = Color(0xFF003A3F); + static const cyan900 = Color(0xFF002124); + static const green50 = Color(0xFFE1FFEE); + static const green100 = Color(0xFFBDFCDB); + static const green150 = Color(0xFF8FEBBD); + static const green200 = Color(0xFF59DEA3); + static const green300 = Color(0xFF00C384); + static const green400 = Color(0xFF00A46E); + static const green500 = Color(0xFF277E59); + static const green600 = Color(0xFF006643); + static const green700 = Color(0xFF004F33); + static const green800 = Color(0xFF003A25); + static const green900 = Color(0xFF002213); + static const purple50 = Color(0xFFF7F8FF); + static const purple100 = Color(0xFFECEDFF); + static const purple150 = Color(0xFFD4D7FF); + static const purple200 = Color(0xFFC1C5FF); + static const purple300 = Color(0xFFA1A3FF); + static const purple400 = Color(0xFF8482FC); + static const purple500 = Color(0xFF644AF9); + static const purple600 = Color(0xFF553BD8); + static const purple700 = Color(0xFF4032A1); + static const purple800 = Color(0xFF2E2576); + static const purple900 = Color(0xFF1A114D); + static const yellow50 = Color(0xFFFEF9DA); + static const yellow100 = Color(0xFFFCEDB9); + static const yellow150 = Color(0xFFFCD579); + static const yellow200 = Color(0xFFF6BF57); + static const yellow300 = Color(0xFFFA922B); + static const yellow400 = Color(0xFFF26D10); + static const yellow500 = Color(0xFFC84801); + static const yellow600 = Color(0xFFA82C00); + static const yellow700 = Color(0xFF842106); + static const yellow800 = Color(0xFF5F1A05); + static const yellow900 = Color(0xFF331302); + static const red50 = Color(0xFFFFF5FA); + static const red100 = Color(0xFFFFE7F2); + static const red150 = Color(0xFFFFCCDF); + static const red200 = Color(0xFFFFB1CD); + static const red300 = Color(0xFFFE87A1); + static const red400 = Color(0xFFFC526A); + static const red500 = Color(0xFFD90D10); + static const red600 = Color(0xFFB3093C); + static const red700 = Color(0xFF890D37); + static const red800 = Color(0xFF68052B); + static const red900 = Color(0xFF3E021A); + static const violet50 = Color(0xFFFEF4FF); + static const violet100 = Color(0xFFFBE8FE); + static const violet150 = Color(0xFFF7CFFC); + static const violet200 = Color(0xFFEEB5F4); + static const violet300 = Color(0xFFE68BEC); + static const violet400 = Color(0xFFD75FE7); + static const violet500 = Color(0xFFB716CA); + static const violet600 = Color(0xFF9D00AE); + static const violet700 = Color(0xFF7C0089); + static const violet800 = Color(0xFF5C0066); + static const violet900 = Color(0xFF36003D); + static const lime50 = Color(0xFFF1FDE8); + static const lime100 = Color(0xFFD4FFB0); + static const lime150 = Color(0xFFB1EE79); + static const lime200 = Color(0xFF9CDA5D); + static const lime300 = Color(0xFF78C100); + static const lime400 = Color(0xFF639E11); + static const lime500 = Color(0xFF4B7A0A); + static const lime600 = Color(0xFF3E6213); + static const lime700 = Color(0xFF355315); + static const lime800 = Color(0xFF203A00); + static const lime900 = Color(0xFF112100); + static const size2 = 2; + static const size4 = 4; + static const size6 = 6; + static const size8 = 8; + static const size12 = 12; + static const size16 = 16; + static const size20 = 20; + static const size24 = 24; + static const size32 = 32; + static const size40 = 40; + static const size48 = 48; + static const size64 = 64; + static const size28 = 28; + static const size80 = 80; + static const size128 = 128; + static const size240 = 240; + static const size320 = 320; + static const size480 = 480; + static const size560 = 560; + static const size640 = 640; + static const radius0 = 0; + static const radius2 = 2; + static const radius4 = 4; + static const radius6 = 6; + static const radius8 = 8; + static const radius12 = 12; + static const radius16 = 16; + static const radius20 = 20; + static const radius24 = 24; + static const radius32 = 32; + static const radiusFull = 9999; + static const space0 = 0; + static const space2 = 2; + static const space4 = 4; + static const space8 = 8; + static const space12 = 12; + static const space16 = 16; + static const space20 = 20; + static const space24 = 24; + static const space32 = 32; + static const space40 = 40; + static const space48 = 48; + static const space64 = 64; + static const space80 = 80; + static const w100 = 1; + static const w150 = 1.5; + static const w200 = 2; + static const w300 = 3; + static const w400 = 4; + static const w120 = 1.2; + static const fontFamilyGeist = 'Geist'; + static const fontFamilyGeistMono = 'Geist Mono'; + static const fontFamilySfPro = 'SF Pro'; + static const fontFamilySfMono = 'SF Mono'; + static const fontFamilyRoboto = 'Roboto'; + static const fontFamilyRobotoMono = 'Roboto Mono'; + static const fontWeightW400 = 400; + static const fontWeightW500 = 500; + static const fontWeightW600 = 600; + static const fontWeightW700 = 700; + static const fontSizeSize10 = 10; + static const fontSizeSize12 = 12; + static const fontSizeSize14 = 14; + static const fontSizeSize16 = 16; + static const fontSizeSize15 = 15; + static const fontSizeSize17 = 17; + static const fontSizeSize18 = 18; + static const fontSizeSize20 = 20; + static const fontSizeSize24 = 24; + static const fontSizeSize28 = 28; + static const fontSizeSize32 = 32; + static const fontSizeSize40 = 40; + static const fontSizeSize48 = 48; + static const fontSizeSize13 = 13; + static const fontSizeSize8 = 8; + static const lineHeightLineHeight8 = 8; + static const lineHeightLineHeight10 = 10; + static const lineHeightLineHeight12 = 12; + static const lineHeightLineHeight14 = 14; + static const lineHeightLineHeight15 = 15; + static const lineHeightLineHeight16 = 16; + static const lineHeightLineHeight17 = 17; + static const lineHeightLineHeight18 = 18; + static const lineHeightLineHeight20 = 20; + static const lineHeightLineHeight24 = 24; + static const lineHeightLineHeight28 = 28; + static const lineHeightLineHeight32 = 32; + static const lineHeightLineHeight40 = 40; + static const lineHeightLineHeight48 = 48; + static const typographyFontFamilySans = 'Geist'; + static const typographyFontFamilyMono = 'Geist Mono'; + static const typographyFontWeightRegular = 400; + static const typographyFontWeightMedium = 500; + static const typographyFontWeightSemiBold = 600; + static const typographyFontWeightBold = 700; + static const typographyFontSizeXxs = 10; + static const typographyFontSizeXs = 12; + static const typographyFontSizeSm = 14; + static const typographyFontSizeMd = 16; + static const typographyFontSizeLg = 18; + static const typographyFontSizeXl = 20; + static const typographyFontSize2xl = 24; + static const typographyFontSizeMicro = 8; + static const typographyLineHeightTight = 16; + static const typographyLineHeightNormal = 20; + static const typographyLineHeightRelaxed = 24; + static final lightElevation0 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0), + ), + ]; + static final lightElevation1 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.05), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.1), + blurRadius: 2, + offset: Offset(0, 1), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.06), + blurRadius: 8, + offset: Offset(0, 4), + ), + ]; + static final lightElevation2 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.05), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.12), + blurRadius: 4, + offset: Offset(0, 2), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.06), + blurRadius: 16, + offset: Offset(0, 6), + ), + ]; + static final lightElevation3 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.05), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.14), + blurRadius: 8, + offset: Offset(0, 4), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.1), + blurRadius: 24, + offset: Offset(0, 12), + ), + ]; + static final lightElevation4 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.05), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.16), + blurRadius: 12, + offset: Offset(0, 6), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.12), + blurRadius: 32, + offset: Offset(0, 20), + ), + ]; + static final darkElevation0 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0), + ), + ]; + static final darkElevation1 = [ + const BoxShadow( + color: Color.fromRGBO(255, 255, 255, 0.15), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.2), + blurRadius: 2, + offset: Offset(0, 1), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.1), + blurRadius: 8, + offset: Offset(0, 4), + ), + ]; + static final darkElevation2 = [ + const BoxShadow( + color: Color.fromRGBO(255, 255, 255, 0.15), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.22), + blurRadius: 4, + offset: Offset(0, 2), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.12), + blurRadius: 16, + offset: Offset(0, 6), + ), + ]; + static final darkElevation3 = [ + const BoxShadow( + color: Color.fromRGBO(255, 255, 255, 0.15), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.24), + blurRadius: 8, + offset: Offset(0, 4), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.14), + blurRadius: 24, + offset: Offset(0, 12), + ), + ]; + static final darkElevation4 = [ + const BoxShadow( + color: Color.fromRGBO(255, 255, 255, 0.15), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.28), + blurRadius: 12, + offset: Offset(0, 6), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.16), + blurRadius: 32, + offset: Offset(0, 20), + ), + ]; + static const radiusNone = 0; + static const radiusXxs = 2; + static const radiusXs = 4; + static const radiusSm = 6; + static const radiusMd = 8; + static const radiusLg = 12; + static const radiusXl = 16; + static const radius2xl = 20; + static const radiusMax = 9999; + static const radius3xl = 24; + static const radius4xl = 32; + static const spacingNone = 0; + static const spacingXxs = 4; + static const spacingXs = 8; + static const spacingSm = 12; + static const spacingMd = 16; + static const spacingXl = 24; + static const spacing2xl = 32; + static const spacing3xl = 40; + static const spacingLg = 20; + static const spacingXxxs = 2; + static const deviceRadius = 8; + static const deviceSafeAreaBottom = 0; + static const deviceSafeAreaTop = 0; + static const messageBubbleRadiusGroupTop = 20; + static const messageBubbleRadiusGroupMiddle = 20; + static const messageBubbleRadiusGroupBottom = 20; + static const messageBubbleRadiusTail = 0; + static const messageBubbleRadiusAttachment = 12; + static const messageBubbleRadiusAttachmentInline = 8; + static const composerRadiusFixed = 24; + static const composerRadiusFloating = 24; + static const composerBg = Color(0xFF1C1C1C); + static const buttonRadiusLg = 9999; + static const buttonRadiusMd = 9999; + static const buttonRadiusSm = 9999; + static const buttonRadiusFull = 9999; + static const buttonVisualHeightSm = 32; + static const buttonVisualHeightMd = 40; + static const buttonVisualHeightLg = 48; + static const buttonHitTargetMinHeight = 48; + static const buttonHitTargetMinWidth = 48; + static const buttonPaddingYLg = 14; + static const buttonPaddingYMd = 10; + static const buttonPaddingYSm = 6; + static const buttonPaddingXIconOnlyLg = 14; + static const buttonPaddingXIconOnlyMd = 10; + static const buttonPaddingXIconOnlySm = 6; + static const buttonPaddingXWithLabelLg = 16; + static const buttonPaddingXWithLabelMd = 16; + static const buttonPaddingXWithLabelSm = 16; + static const buttonPrimaryBg = Color(0xFF4586FF); + static const buttonPrimaryTextOnAccent = Color(0xFFFFFFFF); + static const buttonPrimaryText = Color(0xFF4586FF); + static const buttonPrimaryBgLiquidGlass = Color(0x00FFFFFF); + static const buttonPrimaryBorder = Color(0xFF19418D); + static const buttonSecondaryText = Color(0xFFFFFFFF); + static const buttonSecondaryBgLiquidGlass = Color(0xFF000000); + static const buttonSecondaryBorder = Color(0xFF464646); + static const buttonSecondaryBg = Color(0xFF1C1C1C); + static const buttonSecondaryTextOnAccent = Color(0xFFFFFFFF); + static const buttonDestructiveText = Color(0xFFD90D10); + static const buttonDestructiveBg = Color(0xFFD90D10); + static const buttonDestructiveTextOnAccent = Color(0xFFFFFFFF); + static const buttonDestructiveBgLiquidGlass = Color(0xFF000000); + static const buttonDestructiveBorder = Color(0xFFD90D10); + static const iconSizeXs = 12; + static const iconSizeSm = 16; + static const iconSizeMd = 20; + static const iconSizeLg = 32; + static const iconStrokeSubtle = 1.2; + static const iconStrokeDefault = 1.5; + static const iconStrokeEmphasis = 2; + static const backgroundCoreHover = Color(0x0DFFFFFF); + static const backgroundCorePressed = Color(0x1AFFFFFF); + static const backgroundCoreSelected = Color(0x26FFFFFF); + static const backgroundCoreDisabled = Color(0xFF1C1C1C); + static const backgroundCoreApp = Color(0xFF000000); + static const backgroundCoreScrim = Color(0x80000000); + static const backgroundCoreSurface = Color(0xFF323232); + static const backgroundCoreSurfaceSubtle = Color(0xFF1C1C1C); + static const backgroundCoreSurfaceStrong = Color(0xFF464646); + static const backgroundCoreOverlay = Color(0xBF000000); + static const backgroundElevationElevation0 = Color(0xFF000000); + static const backgroundElevationElevation1 = Color(0xFF1C1C1C); + static const backgroundElevationElevation2 = Color(0xFF323232); + static const backgroundElevationElevation3 = Color(0xFF464646); + static const backgroundElevationElevation4 = Color(0xFF565656); + static const borderUtilityFocus = Color(0xFFA5C5FF); + static const borderUtilityError = Color(0xFFD90D10); + static const borderUtilityWarning = Color(0xFFF26D10); + static const borderUtilitySuccess = Color(0xFF00C384); + static const borderUtilitySelected = Color(0xFF4586FF); + static const borderUtilityDisabled = Color(0xFF323232); + static const borderCoreDefault = Color(0xFF464646); + static const borderCoreSubtle = Color(0xFF323232); + static const borderCoreStrong = Color(0xFF565656); + static const borderCoreOnDark = Color(0xFFFFFFFF); + static const borderCoreOnAccent = Color(0xFFFFFFFF); + static const borderCoreOpacity10 = Color(0x33FFFFFF); + static const borderCoreOpacity25 = Color(0x40FFFFFF); + static const chatBgIncoming = Color(0xFF323232); + static const chatBgOutgoing = Color(0xFF19418D); + static const chatBgAttachmentIncoming = Color(0xFF464646); + static const chatBgAttachmentOutgoing = Color(0xFF1B53BD); + static const chatBgTypingIndicator = Color(0xFF565656); + static const chatTextMessage = Color(0xFFFFFFFF); + static const chatTextTimestamp = Color(0xFF8F8F8F); + static const chatTextUsername = Color(0xFFABABAB); + static const chatTextMention = Color(0xFFFFFFFF); + static const chatTextLink = Color(0xFFFFFFFF); + static const chatTextReaction = Color(0xFFABABAB); + static const chatTextSystem = Color(0xFFABABAB); + static const chatBorderOutgoing = Color(0x00FFFFFF); + static const chatBorderIncoming = Color(0x00FFFFFF); + static const chatBorderOnChatOutgoing = Color(0xFF78A8FF); + static const chatBorderOnChatIncoming = Color(0xFF545969); + static const chatReplyIndicatorIncoming = Color(0xFF6A6A6A); + static const chatReplyIndicatorOutgoing = Color(0xFFA5C5FF); + static const chatWaveformBar = Color(0x40FFFFFF); + static const chatWaveformBarPlaying = Color(0xFF4586FF); + static const chatPollProgressTrackIncoming = Color(0xFFFFFFFF); + static const chatPollProgressTrackOutgoing = Color(0xFFFFFFFF); + static const chatPollProgressFillIncoming = Color(0xFF565656); + static const chatPollProgressFillOutgoing = Color(0xFF005FFF); + static const chatThreadConnectorIncoming = Color(0xFF323232); + static const chatThreadConnectorOutgoing = Color(0xFF19418D); + static const inputBorderDefault = Color(0xFF464646); + static const inputBorderHover = Color(0xFF565656); + static const inputBorderFocus = Color(0xFF4586FF); + static const inputTextDefault = Color(0xFFFFFFFF); + static const inputTextPlaceholder = Color(0xFF8F8F8F); + static const inputTextIcon = Color(0xFF8F8F8F); + static const inputTextDisabled = Color(0xFF565656); + static const inputSendIcon = Color(0xFF4586FF); + static const inputSendIconDisabled = Color(0xFF565656); + static const reactionBg = Color(0xFF1C1C1C); + static const reactionBorder = Color(0xFF464646); + static const reactionText = Color(0xFFFFFFFF); + static const reactionEmoji = Color(0xFFFFFFFF); + static const presenceBgOnline = Color(0xFF00C384); + static const presenceBorder = Color(0xFFFFFFFF); + static const presenceBgOffline = Color(0xFF565656); + static const systemText = Color(0xFFFFFFFF); + static const systemBgBlur = Color(0x03000000); + static const systemScrollbar = Color(0x80FFFFFF); + static const systemCaret = Color(0xFFFFFFFF); + static const badgeBgPrimary = Color(0xFF4586FF); + static const badgeBorder = Color(0xFFFFFFFF); + static const badgeBgError = Color(0xFFD90D10); + static const badgeBgNeutral = Color(0xFF565656); + static const badgeText = Color(0xFFFFFFFF); + static const badgeTextInverse = Color(0xFFFFFFFF); + static const badgeBgDefault = Color(0xFF1C1C1C); + static const badgeBgInverse = Color(0xFF000000); + static const controlRadiocheckBg = Color(0x00FFFFFF); + static const controlRadiocheckBorder = Color(0xFF464646); + static const controlRadiocheckBgSelected = Color(0xFFFFFFFF); + static const controlRadiocheckIconSelected = Color(0xFF000000); + static const controlRemoveControlBg = Color(0xFF000000); + static const controlRemoveControlIcon = Color(0xFFFFFFFF); + static const controlRemoveControlBorder = Color(0xFFFFFFFF); + static const controlProgressBarTrack = Color(0xFFF8F8F8); + static const controlProgressBarFill = Color(0xFF323232); + static const controlPlayControlBg = Color(0xFF1C1C1C); + static const controlPlayControlIcon = Color(0xFFFFFFFF); + static const controlPlayControlBorder = Color(0xFF464646); + static const controlPlayControlBgInverse = Color(0xFF000000); + static const controlPlayControlIconInverse = Color(0xFFFFFFFF); + static const controlToggleSwitchBgSelected = Color(0xFF4586FF); + static const controlToggleSwitchKnob = Color(0xFF565656); + static const controlToggleSwitchBg = Color(0xFF464646); + static const controlToggleSwitchBgDisabled = Color(0xFF1C1C1C); + static const textPrimary = Color(0xFFFFFFFF); + static const textSecondary = Color(0xFFABABAB); + static const textTertiary = Color(0xFF8F8F8F); + static const textInverse = Color(0xFF000000); + static const textDisabled = Color(0xFF565656); + static const textLink = Color(0xFFFFFFFF); + static const textOnAccent = Color(0xFFFFFFFF); + static const avatarPaletteBg1 = Color(0xFF142F63); + static const avatarPaletteBg2 = Color(0xFF003A3F); + static const avatarPaletteBg3 = Color(0xFF003A25); + static const avatarPaletteBg4 = Color(0xFF2E2576); + static const avatarPaletteBg5 = Color(0xFF5F1A05); + static const avatarPaletteText1 = Color(0xFFE3EDFF); + static const avatarPaletteText2 = Color(0xFFD1F3F6); + static const avatarPaletteText3 = Color(0xFFBDFCDB); + static const avatarPaletteText4 = Color(0xFFECEDFF); + static const avatarPaletteText5 = Color(0xFFFCEDB9); + static const avatarBgDefault = Color(0xFF142F63); + static const avatarBgPlaceholder = Color(0xFF464646); + static const avatarTextDefault = Color(0xFFE3EDFF); + static const avatarTextPlaceholder = Color(0xFF8F8F8F); + static const accentPrimary = Color(0xFF4586FF); + static const accentSuccess = Color(0xFF00C384); + static const accentWarning = Color(0xFFF26D10); + static const accentError = Color(0xFFD90D10); + static const accentNeutral = Color(0xFF565656); + static const accentBlack = Color(0xFF000000); + static const brand50 = Color(0xFF091A3B); + static const brand100 = Color(0xFF142F63); + static const brand150 = Color(0xFF19418D); + static const brand200 = Color(0xFF19418D); + static const brand300 = Color(0xFF1B53BD); + static const brand400 = Color(0xFF005FFF); + static const brand500 = Color(0xFF4586FF); + static const brand600 = Color(0xFF78A8FF); + static const brand700 = Color(0xFFA5C5FF); + static const brand800 = Color(0xFFC3D9FF); + static const brand900 = Color(0xFFE3EDFF); +} diff --git a/packages/stream_core_flutter/lib/src/theme/primitives/tokens/light/stream_tokens.dart b/packages/stream_core_flutter/lib/src/theme/primitives/tokens/light/stream_tokens.dart new file mode 100644 index 0000000..2637230 --- /dev/null +++ b/packages/stream_core_flutter/lib/src/theme/primitives/tokens/light/stream_tokens.dart @@ -0,0 +1,572 @@ +import 'package:flutter/widgets.dart'; + +class StreamTokens { + StreamTokens._(); + + static const baseTransparent0 = Color(0x00FFFFFF); + static const baseTransparentBlack5 = Color(0x0D000000); + static const baseTransparentBlack10 = Color(0x1A000000); + static const baseTransparentWhite70 = Color(0xB3FFFFFF); + static const baseTransparentWhite10 = Color(0x1AFFFFFF); + static const baseTransparentWhite20 = Color(0x33FFFFFF); + static const baseTransparentBlack70 = Color(0xB3000000); + static const baseBlack = Color(0xFF000000); + static const baseWhite = Color(0xFFFFFFFF); + static const slate50 = Color(0xFFF6F8FA); + static const slate100 = Color(0xFFEBEEF1); + static const slate150 = Color(0xFFD5DBE1); + static const slate200 = Color(0xFFC0C8D2); + static const slate300 = Color(0xFFA3ACBA); + static const slate400 = Color(0xFF87909F); + static const slate500 = Color(0xFF687385); + static const slate600 = Color(0xFF545969); + static const slate700 = Color(0xFF414552); + static const slate800 = Color(0xFF30313D); + static const slate900 = Color(0xFF1A1B25); + static const neutral50 = Color(0xFFF8F8F8); + static const neutral100 = Color(0xFFEFEFEF); + static const neutral150 = Color(0xFFD8D8D8); + static const neutral200 = Color(0xFFC4C4C4); + static const neutral300 = Color(0xFFABABAB); + static const neutral400 = Color(0xFF8F8F8F); + static const neutral500 = Color(0xFF6A6A6A); + static const neutral600 = Color(0xFF565656); + static const neutral700 = Color(0xFF464646); + static const neutral800 = Color(0xFF323232); + static const neutral900 = Color(0xFF1C1C1C); + static const blue50 = Color(0xFFF3F7FF); + static const blue100 = Color(0xFFE3EDFF); + static const blue150 = Color(0xFFC3D9FF); + static const blue200 = Color(0xFFA5C5FF); + static const blue300 = Color(0xFF78A8FF); + static const blue400 = Color(0xFF4586FF); + static const blue500 = Color(0xFF005FFF); + static const blue600 = Color(0xFF1B53BD); + static const blue700 = Color(0xFF19418D); + static const blue800 = Color(0xFF142F63); + static const blue900 = Color(0xFF091A3B); + static const cyan50 = Color(0xFFF1FBFC); + static const cyan100 = Color(0xFFD1F3F6); + static const cyan150 = Color(0xFFA9E4EA); + static const cyan200 = Color(0xFF72D7E0); + static const cyan300 = Color(0xFF45BCC7); + static const cyan400 = Color(0xFF1E9EA9); + static const cyan500 = Color(0xFF248088); + static const cyan600 = Color(0xFF006970); + static const cyan700 = Color(0xFF065056); + static const cyan800 = Color(0xFF003A3F); + static const cyan900 = Color(0xFF002124); + static const green50 = Color(0xFFE1FFEE); + static const green100 = Color(0xFFBDFCDB); + static const green150 = Color(0xFF8FEBBD); + static const green200 = Color(0xFF59DEA3); + static const green300 = Color(0xFF00C384); + static const green400 = Color(0xFF00A46E); + static const green500 = Color(0xFF277E59); + static const green600 = Color(0xFF006643); + static const green700 = Color(0xFF004F33); + static const green800 = Color(0xFF003A25); + static const green900 = Color(0xFF002213); + static const purple50 = Color(0xFFF7F8FF); + static const purple100 = Color(0xFFECEDFF); + static const purple150 = Color(0xFFD4D7FF); + static const purple200 = Color(0xFFC1C5FF); + static const purple300 = Color(0xFFA1A3FF); + static const purple400 = Color(0xFF8482FC); + static const purple500 = Color(0xFF644AF9); + static const purple600 = Color(0xFF553BD8); + static const purple700 = Color(0xFF4032A1); + static const purple800 = Color(0xFF2E2576); + static const purple900 = Color(0xFF1A114D); + static const yellow50 = Color(0xFFFEF9DA); + static const yellow100 = Color(0xFFFCEDB9); + static const yellow150 = Color(0xFFFCD579); + static const yellow200 = Color(0xFFF6BF57); + static const yellow300 = Color(0xFFFA922B); + static const yellow400 = Color(0xFFF26D10); + static const yellow500 = Color(0xFFC84801); + static const yellow600 = Color(0xFFA82C00); + static const yellow700 = Color(0xFF842106); + static const yellow800 = Color(0xFF5F1A05); + static const yellow900 = Color(0xFF331302); + static const red50 = Color(0xFFFFF5FA); + static const red100 = Color(0xFFFFE7F2); + static const red150 = Color(0xFFFFCCDF); + static const red200 = Color(0xFFFFB1CD); + static const red300 = Color(0xFFFE87A1); + static const red400 = Color(0xFFFC526A); + static const red500 = Color(0xFFD90D10); + static const red600 = Color(0xFFB3093C); + static const red700 = Color(0xFF890D37); + static const red800 = Color(0xFF68052B); + static const red900 = Color(0xFF3E021A); + static const violet50 = Color(0xFFFEF4FF); + static const violet100 = Color(0xFFFBE8FE); + static const violet150 = Color(0xFFF7CFFC); + static const violet200 = Color(0xFFEEB5F4); + static const violet300 = Color(0xFFE68BEC); + static const violet400 = Color(0xFFD75FE7); + static const violet500 = Color(0xFFB716CA); + static const violet600 = Color(0xFF9D00AE); + static const violet700 = Color(0xFF7C0089); + static const violet800 = Color(0xFF5C0066); + static const violet900 = Color(0xFF36003D); + static const lime50 = Color(0xFFF1FDE8); + static const lime100 = Color(0xFFD4FFB0); + static const lime150 = Color(0xFFB1EE79); + static const lime200 = Color(0xFF9CDA5D); + static const lime300 = Color(0xFF78C100); + static const lime400 = Color(0xFF639E11); + static const lime500 = Color(0xFF4B7A0A); + static const lime600 = Color(0xFF3E6213); + static const lime700 = Color(0xFF355315); + static const lime800 = Color(0xFF203A00); + static const lime900 = Color(0xFF112100); + static const size2 = 2; + static const size4 = 4; + static const size6 = 6; + static const size8 = 8; + static const size12 = 12; + static const size16 = 16; + static const size20 = 20; + static const size24 = 24; + static const size32 = 32; + static const size40 = 40; + static const size48 = 48; + static const size64 = 64; + static const size28 = 28; + static const size80 = 80; + static const size128 = 128; + static const size240 = 240; + static const size320 = 320; + static const size480 = 480; + static const size560 = 560; + static const size640 = 640; + static const radius0 = 0; + static const radius2 = 2; + static const radius4 = 4; + static const radius6 = 6; + static const radius8 = 8; + static const radius12 = 12; + static const radius16 = 16; + static const radius20 = 20; + static const radius24 = 24; + static const radius32 = 32; + static const radiusFull = 9999; + static const space0 = 0; + static const space2 = 2; + static const space4 = 4; + static const space8 = 8; + static const space12 = 12; + static const space16 = 16; + static const space20 = 20; + static const space24 = 24; + static const space32 = 32; + static const space40 = 40; + static const space48 = 48; + static const space64 = 64; + static const space80 = 80; + static const w100 = 1; + static const w150 = 1.5; + static const w200 = 2; + static const w300 = 3; + static const w400 = 4; + static const w120 = 1.2; + static const fontFamilyGeist = 'Geist'; + static const fontFamilyGeistMono = 'Geist Mono'; + static const fontFamilySfPro = 'SF Pro'; + static const fontFamilySfMono = 'SF Mono'; + static const fontFamilyRoboto = 'Roboto'; + static const fontFamilyRobotoMono = 'Roboto Mono'; + static const fontWeightW400 = 400; + static const fontWeightW500 = 500; + static const fontWeightW600 = 600; + static const fontWeightW700 = 700; + static const fontSizeSize10 = 10; + static const fontSizeSize12 = 12; + static const fontSizeSize14 = 14; + static const fontSizeSize16 = 16; + static const fontSizeSize15 = 15; + static const fontSizeSize17 = 17; + static const fontSizeSize18 = 18; + static const fontSizeSize20 = 20; + static const fontSizeSize24 = 24; + static const fontSizeSize28 = 28; + static const fontSizeSize32 = 32; + static const fontSizeSize40 = 40; + static const fontSizeSize48 = 48; + static const fontSizeSize13 = 13; + static const fontSizeSize8 = 8; + static const lineHeightLineHeight8 = 8; + static const lineHeightLineHeight10 = 10; + static const lineHeightLineHeight12 = 12; + static const lineHeightLineHeight14 = 14; + static const lineHeightLineHeight15 = 15; + static const lineHeightLineHeight16 = 16; + static const lineHeightLineHeight17 = 17; + static const lineHeightLineHeight18 = 18; + static const lineHeightLineHeight20 = 20; + static const lineHeightLineHeight24 = 24; + static const lineHeightLineHeight28 = 28; + static const lineHeightLineHeight32 = 32; + static const lineHeightLineHeight40 = 40; + static const lineHeightLineHeight48 = 48; + static const typographyFontFamilySans = 'Geist'; + static const typographyFontFamilyMono = 'Geist Mono'; + static const typographyFontWeightRegular = 400; + static const typographyFontWeightMedium = 500; + static const typographyFontWeightSemiBold = 600; + static const typographyFontWeightBold = 700; + static const typographyFontSizeXxs = 10; + static const typographyFontSizeXs = 12; + static const typographyFontSizeSm = 14; + static const typographyFontSizeMd = 16; + static const typographyFontSizeLg = 18; + static const typographyFontSizeXl = 20; + static const typographyFontSize2xl = 24; + static const typographyFontSizeMicro = 8; + static const typographyLineHeightTight = 16; + static const typographyLineHeightNormal = 20; + static const typographyLineHeightRelaxed = 24; + static final lightElevation0 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0), + ), + ]; + static final lightElevation1 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.05), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.1), + blurRadius: 2, + offset: Offset(0, 1), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.06), + blurRadius: 8, + offset: Offset(0, 4), + ), + ]; + static final lightElevation2 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.05), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.12), + blurRadius: 4, + offset: Offset(0, 2), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.06), + blurRadius: 16, + offset: Offset(0, 6), + ), + ]; + static final lightElevation3 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.05), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.14), + blurRadius: 8, + offset: Offset(0, 4), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.1), + blurRadius: 24, + offset: Offset(0, 12), + ), + ]; + static final lightElevation4 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.05), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.16), + blurRadius: 12, + offset: Offset(0, 6), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.12), + blurRadius: 32, + offset: Offset(0, 20), + ), + ]; + static final darkElevation0 = [ + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0), + ), + ]; + static final darkElevation1 = [ + const BoxShadow( + color: Color.fromRGBO(255, 255, 255, 0.15), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.2), + blurRadius: 2, + offset: Offset(0, 1), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.1), + blurRadius: 8, + offset: Offset(0, 4), + ), + ]; + static final darkElevation2 = [ + const BoxShadow( + color: Color.fromRGBO(255, 255, 255, 0.15), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.22), + blurRadius: 4, + offset: Offset(0, 2), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.12), + blurRadius: 16, + offset: Offset(0, 6), + ), + ]; + static final darkElevation3 = [ + const BoxShadow( + color: Color.fromRGBO(255, 255, 255, 0.15), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.24), + blurRadius: 8, + offset: Offset(0, 4), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.14), + blurRadius: 24, + offset: Offset(0, 12), + ), + ]; + static final darkElevation4 = [ + const BoxShadow( + color: Color.fromRGBO(255, 255, 255, 0.15), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.28), + blurRadius: 12, + offset: Offset(0, 6), + ), + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.16), + blurRadius: 32, + offset: Offset(0, 20), + ), + ]; + static const radiusNone = 0; + static const radiusXxs = 2; + static const radiusXs = 4; + static const radiusSm = 6; + static const radiusMd = 8; + static const radiusLg = 12; + static const radiusXl = 16; + static const radius2xl = 20; + static const radiusMax = 9999; + static const radius3xl = 24; + static const radius4xl = 32; + static const spacingNone = 0; + static const spacingXxs = 4; + static const spacingXs = 8; + static const spacingSm = 12; + static const spacingMd = 16; + static const spacingXl = 24; + static const spacing2xl = 32; + static const spacing3xl = 40; + static const spacingLg = 20; + static const spacingXxxs = 2; + static const deviceRadius = 8; + static const deviceSafeAreaBottom = 0; + static const deviceSafeAreaTop = 0; + static const messageBubbleRadiusGroupTop = 20; + static const messageBubbleRadiusGroupMiddle = 20; + static const messageBubbleRadiusGroupBottom = 20; + static const messageBubbleRadiusTail = 0; + static const messageBubbleRadiusAttachment = 12; + static const messageBubbleRadiusAttachmentInline = 8; + static const composerRadiusFixed = 24; + static const composerRadiusFloating = 24; + static const composerBg = Color(0xFFFFFFFF); + static const buttonRadiusLg = 9999; + static const buttonRadiusMd = 9999; + static const buttonRadiusSm = 9999; + static const buttonRadiusFull = 9999; + static const buttonVisualHeightSm = 32; + static const buttonVisualHeightMd = 40; + static const buttonVisualHeightLg = 48; + static const buttonHitTargetMinHeight = 48; + static const buttonHitTargetMinWidth = 48; + static const buttonPaddingYLg = 14; + static const buttonPaddingYMd = 10; + static const buttonPaddingYSm = 6; + static const buttonPaddingXIconOnlyLg = 14; + static const buttonPaddingXIconOnlyMd = 10; + static const buttonPaddingXIconOnlySm = 6; + static const buttonPaddingXWithLabelLg = 16; + static const buttonPaddingXWithLabelMd = 16; + static const buttonPaddingXWithLabelSm = 16; + static const buttonPrimaryBg = Color(0xFF005FFF); + static const buttonPrimaryTextOnAccent = Color(0xFFFFFFFF); + static const buttonPrimaryText = Color(0xFF005FFF); + static const buttonPrimaryBgLiquidGlass = Color(0x00FFFFFF); + static const buttonPrimaryBorder = Color(0xFFA5C5FF); + static const buttonSecondaryText = Color(0xFF1A1B25); + static const buttonSecondaryBgLiquidGlass = Color(0xFFFFFFFF); + static const buttonSecondaryBorder = Color(0xFFD5DBE1); + static const buttonSecondaryBg = Color(0xFFEBEEF1); + static const buttonSecondaryTextOnAccent = Color(0xFF1A1B25); + static const buttonDestructiveText = Color(0xFFD90D10); + static const buttonDestructiveBg = Color(0xFFD90D10); + static const buttonDestructiveTextOnAccent = Color(0xFFFFFFFF); + static const buttonDestructiveBgLiquidGlass = Color(0xFFFFFFFF); + static const buttonDestructiveBorder = Color(0xFFD90D10); + static const iconSizeXs = 12; + static const iconSizeSm = 16; + static const iconSizeMd = 20; + static const iconSizeLg = 32; + static const iconStrokeSubtle = 1.2; + static const iconStrokeDefault = 1.5; + static const iconStrokeEmphasis = 2; + static const backgroundCoreHover = Color(0x0D1E252B); + static const backgroundCorePressed = Color(0x1A1E252B); + static const backgroundCoreSelected = Color(0x261E252B); + static const backgroundCoreDisabled = Color(0xFFEBEEF1); + static const backgroundCoreApp = Color(0xFFFFFFFF); + static const backgroundCoreScrim = Color(0x40000000); + static const backgroundCoreSurface = Color(0xFFC0C8D2); + static const backgroundCoreSurfaceSubtle = Color(0xFFEBEEF1); + static const backgroundCoreSurfaceStrong = Color(0xFFA3ACBA); + static const backgroundCoreOverlay = Color(0xBFFFFFFF); + static const backgroundElevationElevation0 = Color(0xFFFFFFFF); + static const backgroundElevationElevation1 = Color(0xFFFFFFFF); + static const backgroundElevationElevation2 = Color(0xFFFFFFFF); + static const backgroundElevationElevation3 = Color(0xFFFFFFFF); + static const backgroundElevationElevation4 = Color(0xFFFFFFFF); + static const borderUtilityFocus = Color(0xFF78A8FF); + static const borderUtilityError = Color(0xFFD90D10); + static const borderUtilityWarning = Color(0xFFF26D10); + static const borderUtilitySuccess = Color(0xFF00C384); + static const borderUtilitySelected = Color(0xFF005FFF); + static const borderUtilityDisabled = Color(0xFFC0C8D2); + static const borderCoreDefault = Color(0xFFD5DBE1); + static const borderCoreSubtle = Color(0xFFEBEEF1); + static const borderCoreStrong = Color(0xFFC0C8D2); + static const borderCoreOnDark = Color(0xFFFFFFFF); + static const borderCoreOnAccent = Color(0xFFFFFFFF); + static const borderCoreOpacity10 = Color(0x1A000000); + static const borderCoreOpacity25 = Color(0x40000000); + static const chatBgIncoming = Color(0xFFEBEEF1); + static const chatBgOutgoing = Color(0xFFE3EDFF); + static const chatBgAttachmentIncoming = Color(0xFFD5DBE1); + static const chatBgAttachmentOutgoing = Color(0xFFC3D9FF); + static const chatBgTypingIndicator = Color(0xFF687385); + static const chatTextMessage = Color(0xFF1A1B25); + static const chatTextTimestamp = Color(0xFF687385); + static const chatTextUsername = Color(0xFF414552); + static const chatTextMention = Color(0xFF005FFF); + static const chatTextLink = Color(0xFF005FFF); + static const chatTextReaction = Color(0xFF414552); + static const chatTextSystem = Color(0xFF414552); + static const chatBorderOutgoing = Color(0x00FFFFFF); + static const chatBorderIncoming = Color(0x00FFFFFF); + static const chatBorderOnChatOutgoing = Color(0xFF78A8FF); + static const chatBorderOnChatIncoming = Color(0xFF87909F); + static const chatReplyIndicatorIncoming = Color(0xFF87909F); + static const chatReplyIndicatorOutgoing = Color(0xFF4586FF); + static const chatWaveformBar = Color(0x40000000); + static const chatWaveformBarPlaying = Color(0xFF005FFF); + static const chatPollProgressTrackIncoming = Color(0xFF545969); + static const chatPollProgressTrackOutgoing = Color(0xFF005FFF); + static const chatPollProgressFillIncoming = Color(0xFFA3ACBA); + static const chatPollProgressFillOutgoing = Color(0xFFA5C5FF); + static const chatThreadConnectorIncoming = Color(0xFFC0C8D2); + static const chatThreadConnectorOutgoing = Color(0xFFE3EDFF); + static const inputBorderDefault = Color(0xFFD5DBE1); + static const inputBorderHover = Color(0xFFC0C8D2); + static const inputBorderFocus = Color(0xFF005FFF); + static const inputTextDefault = Color(0xFF1A1B25); + static const inputTextPlaceholder = Color(0xFF687385); + static const inputTextIcon = Color(0xFF687385); + static const inputTextDisabled = Color(0xFF87909F); + static const inputSendIcon = Color(0xFF005FFF); + static const inputSendIconDisabled = Color(0xFF87909F); + static const reactionBg = Color(0xFFFFFFFF); + static const reactionBorder = Color(0xFFD5DBE1); + static const reactionText = Color(0xFF1A1B25); + static const reactionEmoji = Color(0xFF1A1B25); + static const presenceBgOnline = Color(0xFF00C384); + static const presenceBorder = Color(0xFFFFFFFF); + static const presenceBgOffline = Color(0xFF687385); + static const systemText = Color(0xFF000000); + static const systemBgBlur = Color(0x03FFFFFF); + static const systemScrollbar = Color(0x80000000); + static const systemCaret = Color(0xFF005FFF); + static const badgeBgPrimary = Color(0xFF005FFF); + static const badgeBorder = Color(0xFFFFFFFF); + static const badgeBgError = Color(0xFFD90D10); + static const badgeBgNeutral = Color(0xFF687385); + static const badgeText = Color(0xFFFFFFFF); + static const badgeTextInverse = Color(0xFF1A1B25); + static const badgeBgDefault = Color(0xFFFFFFFF); + static const badgeBgInverse = Color(0xFF000000); + static const controlRadiocheckBg = Color(0x00FFFFFF); + static const controlRadiocheckBorder = Color(0xFFD5DBE1); + static const controlRadiocheckBgSelected = Color(0xFF005FFF); + static const controlRadiocheckIconSelected = Color(0xFFFFFFFF); + static const controlRemoveControlBg = Color(0xFF000000); + static const controlRemoveControlIcon = Color(0xFFFFFFFF); + static const controlRemoveControlBorder = Color(0xFFFFFFFF); + static const controlProgressBarTrack = Color(0xFF687385); + static const controlProgressBarFill = Color(0xFFC0C8D2); + static const controlPlayControlBg = Color(0xFFFFFFFF); + static const controlPlayControlIcon = Color(0xFF1A1B25); + static const controlPlayControlBorder = Color(0xFFD5DBE1); + static const controlPlayControlBgInverse = Color(0xFF000000); + static const controlPlayControlIconInverse = Color(0xFFFFFFFF); + static const controlToggleSwitchBgSelected = Color(0xFF005FFF); + static const controlToggleSwitchKnob = Color(0xFFFFFFFF); + static const controlToggleSwitchBg = Color(0xFFA3ACBA); + static const controlToggleSwitchBgDisabled = Color(0xFFEBEEF1); + static const textPrimary = Color(0xFF1A1B25); + static const textSecondary = Color(0xFF414552); + static const textTertiary = Color(0xFF687385); + static const textInverse = Color(0xFFFFFFFF); + static const textDisabled = Color(0xFF87909F); + static const textLink = Color(0xFF005FFF); + static const textOnAccent = Color(0xFFFFFFFF); + static const avatarPaletteBg1 = Color(0xFFE3EDFF); + static const avatarPaletteBg2 = Color(0xFFD1F3F6); + static const avatarPaletteBg3 = Color(0xFFBDFCDB); + static const avatarPaletteBg4 = Color(0xFFECEDFF); + static const avatarPaletteBg5 = Color(0xFFFCEDB9); + static const avatarPaletteText1 = Color(0xFF142F63); + static const avatarPaletteText2 = Color(0xFF003A3F); + static const avatarPaletteText3 = Color(0xFF003A25); + static const avatarPaletteText4 = Color(0xFF2E2576); + static const avatarPaletteText5 = Color(0xFF5F1A05); + static const avatarBgDefault = Color(0xFFE3EDFF); + static const avatarBgPlaceholder = Color(0xFFEBEEF1); + static const avatarTextDefault = Color(0xFF142F63); + static const avatarTextPlaceholder = Color(0xFF687385); + static const accentPrimary = Color(0xFF005FFF); + static const accentSuccess = Color(0xFF00C384); + static const accentWarning = Color(0xFFF26D10); + static const accentError = Color(0xFFD90D10); + static const accentNeutral = Color(0xFF687385); + static const accentBlack = Color(0xFF000000); + static const brand50 = Color(0xFFF3F7FF); + static const brand100 = Color(0xFFE3EDFF); + static const brand150 = Color(0xFFC3D9FF); + static const brand200 = Color(0xFFA5C5FF); + static const brand300 = Color(0xFF78A8FF); + static const brand400 = Color(0xFF4586FF); + static const brand500 = Color(0xFF005FFF); + static const brand600 = Color(0xFF1B53BD); + static const brand700 = Color(0xFF19418D); + static const brand800 = Color(0xFF142F63); + static const brand900 = Color(0xFF091A3B); +} diff --git a/packages/stream_core_flutter/lib/src/theme/primitives/tokens/stream_tokens_typography.dart b/packages/stream_core_flutter/lib/src/theme/primitives/tokens/stream_tokens_typography.dart new file mode 100644 index 0000000..3c63f51 --- /dev/null +++ b/packages/stream_core_flutter/lib/src/theme/primitives/tokens/stream_tokens_typography.dart @@ -0,0 +1,114 @@ +import 'package:flutter/widgets.dart'; + +class StreamTokensTypography { + StreamTokensTypography._(); + + static const headingLg = TextStyle( + fontFamily: 'Geist', + fontSize: 20, + fontWeight: FontWeight.w600, + height: 1.2, + ); + static const headingMd = TextStyle( + fontFamily: 'Geist', + fontSize: 18, + fontWeight: FontWeight.w600, + height: 1.1111111111111112, + ); + static const headingSm = TextStyle( + fontFamily: 'Geist', + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1, + ); + static const bodyDefault = TextStyle( + fontFamily: 'Geist', + fontSize: 16, + fontWeight: FontWeight.w400, + height: 1.25, + ); + static const bodyEmphasis = TextStyle( + fontFamily: 'Geist', + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1.25, + ); + static const bodyLink = TextStyle( + fontFamily: 'Geist', + fontSize: 16, + fontWeight: FontWeight.w400, + height: 1.25, + ); + static const bodyLinkEmphasis = TextStyle( + fontFamily: 'Geist', + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1.25, + ); + static const captionDefault = TextStyle( + fontFamily: 'Geist', + fontSize: 14, + fontWeight: FontWeight.w400, + height: 1.1428571428571428, + ); + static const captionEmphasis = TextStyle( + fontFamily: 'Geist', + fontSize: 14, + fontWeight: FontWeight.w600, + height: 1.1428571428571428, + ); + static const captionLink = TextStyle( + fontFamily: 'Geist', + fontSize: 14, + fontWeight: FontWeight.w400, + height: 1.1428571428571428, + ); + static const captionLinkEmphasis = TextStyle( + fontFamily: 'Geist', + fontSize: 14, + fontWeight: FontWeight.w600, + height: 1.1428571428571428, + ); + static const metadataDefault = TextStyle( + fontFamily: 'Geist', + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.3333333333333333, + ); + static const metadataEmphasis = TextStyle( + fontFamily: 'Geist', + fontSize: 12, + fontWeight: FontWeight.w600, + height: 1.3333333333333333, + ); + static const metadataLink = TextStyle( + fontFamily: 'Geist', + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.3333333333333333, + ); + static const metadataLinkEmphasis = TextStyle( + fontFamily: 'Geist', + fontSize: 12, + fontWeight: FontWeight.w600, + height: 1.3333333333333333, + ); + static const numericLg = TextStyle( + fontFamily: 'Geist', + fontSize: 12, + fontWeight: FontWeight.w700, + height: 1, + ); + static const numericMd = TextStyle( + fontFamily: 'Geist', + fontSize: 10, + fontWeight: FontWeight.w700, + height: 1, + ); + static const numericSm = TextStyle( + fontFamily: 'Geist', + fontSize: 8, + fontWeight: FontWeight.w700, + height: 1, + ); +} diff --git a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart index bb68b17..0642452 100644 --- a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart +++ b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:theme_extensions_builder_annotation/theme_extensions_builder_annotation.dart'; import '../../theme/primitives/stream_colors.dart'; +import '../primitives/tokens/dark/stream_tokens.dart' as dark_tokens; +import '../primitives/tokens/light/stream_tokens.dart' as light_tokens; part 'stream_color_scheme.g.theme.dart'; @@ -73,6 +75,7 @@ class StreamColorScheme with _$StreamColorScheme { Color? borderSubtle, Color? borderImage, // Border - Utility + Color? borderDefault, Color? borderFocus, Color? borderDisabled, Color? borderError, @@ -127,6 +130,7 @@ class StreamColorScheme with _$StreamColorScheme { borderImage ??= StreamColors.black10; // Border - Utility + borderDefault ??= StreamColors.slate.shade150; borderFocus ??= brand.shade300; borderDisabled ??= StreamColors.slate.shade100; borderError ??= accentError; @@ -188,6 +192,7 @@ class StreamColorScheme with _$StreamColorScheme { backgroundSurfaceSubtle: backgroundSurfaceSubtle, backgroundSurfaceStrong: backgroundSurfaceStrong, backgroundOverlay: backgroundOverlay, + borderDefault: borderDefault, borderSurface: borderSurface, borderSurfaceSubtle: borderSurfaceSubtle, borderSurfaceStrong: borderSurfaceStrong, @@ -237,6 +242,7 @@ class StreamColorScheme with _$StreamColorScheme { Color? backgroundSurfaceStrong, Color? backgroundOverlay, // Border - Core + Color? borderDefault, Color? borderSurface, Color? borderSurfaceSubtle, Color? borderSurfaceStrong, @@ -290,6 +296,7 @@ class StreamColorScheme with _$StreamColorScheme { backgroundOverlay ??= StreamColors.black50; // Border - Core + borderDefault ??= StreamColors.neutral.shade800; borderSurface ??= StreamColors.neutral.shade500; borderSurfaceSubtle ??= StreamColors.neutral.shade700; borderSurfaceStrong ??= StreamColors.neutral.shade400; @@ -360,6 +367,7 @@ class StreamColorScheme with _$StreamColorScheme { backgroundSurfaceSubtle: backgroundSurfaceSubtle, backgroundSurfaceStrong: backgroundSurfaceStrong, backgroundOverlay: backgroundOverlay, + borderDefault: borderDefault, borderSurface: borderSurface, borderSurfaceSubtle: borderSurfaceSubtle, borderSurfaceStrong: borderSurfaceStrong, @@ -407,6 +415,7 @@ class StreamColorScheme with _$StreamColorScheme { required this.backgroundSurfaceStrong, required this.backgroundOverlay, // Border - Core + required this.borderDefault, required this.borderSurface, required this.borderSurfaceSubtle, required this.borderSurfaceStrong, @@ -498,6 +507,9 @@ class StreamColorScheme with _$StreamColorScheme { // ---- Border colors - Core ---- + /// The default border color. + final Color borderDefault; + /// The standard surface border color. final Color borderSurface; @@ -610,21 +622,21 @@ class StreamBrandColor extends StreamColorSwatch { /// /// Defaults to blue with shade500 as the primary color. factory StreamBrandColor.light() { - final primaryColorValue = StreamColors.blue.shade500.toARGB32(); + final primaryColorValue = light_tokens.StreamTokens.brand500.toARGB32(); return ._( primaryColorValue, { - 50: StreamColors.blue.shade50, - 100: StreamColors.blue.shade100, - 200: StreamColors.blue.shade200, - 300: StreamColors.blue.shade300, - 400: StreamColors.blue.shade400, + 50: light_tokens.StreamTokens.brand50, + 100: light_tokens.StreamTokens.brand100, + 150: light_tokens.StreamTokens.brand150, + 200: light_tokens.StreamTokens.brand200, + 300: light_tokens.StreamTokens.brand300, + 400: light_tokens.StreamTokens.brand400, 500: Color(primaryColorValue), - 600: StreamColors.blue.shade600, - 700: StreamColors.blue.shade700, - 800: StreamColors.blue.shade800, - 900: StreamColors.blue.shade900, - 950: StreamColors.blue.shade950, + 600: light_tokens.StreamTokens.brand600, + 700: light_tokens.StreamTokens.brand700, + 800: light_tokens.StreamTokens.brand800, + 900: light_tokens.StreamTokens.brand900, }, ); } @@ -635,21 +647,21 @@ class StreamBrandColor extends StreamColorSwatch { /// are inverted for dark mode, with lighter shades becoming darker and /// vice versa. factory StreamBrandColor.dark() { - final primaryColorValue = StreamColors.blue.shade400.toARGB32(); + final primaryColorValue = dark_tokens.StreamTokens.brand500.toARGB32(); return ._( primaryColorValue, { - 50: StreamColors.blue.shade900, - 100: StreamColors.blue.shade800, - 200: StreamColors.blue.shade700, - 300: StreamColors.blue.shade600, - 400: StreamColors.blue.shade500, + 50: dark_tokens.StreamTokens.brand50, + 100: dark_tokens.StreamTokens.brand100, + 150: dark_tokens.StreamTokens.brand150, + 200: dark_tokens.StreamTokens.brand200, + 300: dark_tokens.StreamTokens.brand300, + 400: dark_tokens.StreamTokens.brand400, 500: Color(primaryColorValue), - 600: StreamColors.blue.shade300, - 700: StreamColors.blue.shade200, - 800: StreamColors.blue.shade100, - 900: StreamColors.blue.shade50, - 950: StreamColors.white, + 600: dark_tokens.StreamTokens.brand600, + 700: dark_tokens.StreamTokens.brand700, + 800: dark_tokens.StreamTokens.brand800, + 900: dark_tokens.StreamTokens.brand900, }, ); } diff --git a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.g.theme.dart b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.g.theme.dart index e690cea..eaf4c2b 100644 --- a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.g.theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.g.theme.dart @@ -64,6 +64,7 @@ mixin _$StreamColorScheme { b.backgroundOverlay, t, )!, + borderDefault: Color.lerp(a.borderDefault, b.borderDefault, t)!, borderSurface: Color.lerp(a.borderSurface, b.borderSurface, t)!, borderSurfaceSubtle: Color.lerp( a.borderSurfaceSubtle, @@ -115,6 +116,7 @@ mixin _$StreamColorScheme { Color? backgroundSurfaceSubtle, Color? backgroundSurfaceStrong, Color? backgroundOverlay, + Color? borderDefault, Color? borderSurface, Color? borderSurfaceSubtle, Color? borderSurfaceStrong, @@ -160,6 +162,7 @@ mixin _$StreamColorScheme { backgroundSurfaceStrong: backgroundSurfaceStrong ?? _this.backgroundSurfaceStrong, backgroundOverlay: backgroundOverlay ?? _this.backgroundOverlay, + borderDefault: borderDefault ?? _this.borderDefault, borderSurface: borderSurface ?? _this.borderSurface, borderSurfaceSubtle: borderSurfaceSubtle ?? _this.borderSurfaceSubtle, borderSurfaceStrong: borderSurfaceStrong ?? _this.borderSurfaceStrong, @@ -214,6 +217,7 @@ mixin _$StreamColorScheme { backgroundSurfaceSubtle: other.backgroundSurfaceSubtle, backgroundSurfaceStrong: other.backgroundSurfaceStrong, backgroundOverlay: other.backgroundOverlay, + borderDefault: other.borderDefault, borderSurface: other.borderSurface, borderSurfaceSubtle: other.borderSurfaceSubtle, borderSurfaceStrong: other.borderSurfaceStrong, @@ -269,6 +273,7 @@ mixin _$StreamColorScheme { _other.backgroundSurfaceSubtle == _this.backgroundSurfaceSubtle && _other.backgroundSurfaceStrong == _this.backgroundSurfaceStrong && _other.backgroundOverlay == _this.backgroundOverlay && + _other.borderDefault == _this.borderDefault && _other.borderSurface == _this.borderSurface && _other.borderSurfaceSubtle == _this.borderSurfaceSubtle && _other.borderSurfaceStrong == _this.borderSurfaceStrong && @@ -316,6 +321,7 @@ mixin _$StreamColorScheme { _this.backgroundSurfaceSubtle, _this.backgroundSurfaceStrong, _this.backgroundOverlay, + _this.borderDefault, _this.borderSurface, _this.borderSurfaceSubtle, _this.borderSurfaceStrong, 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 581d0d4..d3e6b48 100644 --- a/packages/stream_core_flutter/lib/src/theme/stream_theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/stream_theme.dart @@ -4,7 +4,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:theme_extensions_builder_annotation/theme_extensions_builder_annotation.dart'; +import '../factory/stream_component_factory.dart'; import 'components/stream_avatar_theme.dart'; +import 'components/stream_button_theme.dart'; import 'components/stream_online_indicator_theme.dart'; import 'primitives/stream_radius.dart'; import 'primitives/stream_spacing.dart'; @@ -79,7 +81,9 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { StreamBoxShadow? boxShadow, // Components themes StreamAvatarThemeData? avatarTheme, + StreamButtonThemeData? buttonTheme, StreamOnlineIndicatorThemeData? onlineIndicatorTheme, + StreamComponentFactory? componentFactory, }) { platform ??= defaultTargetPlatform; final isDark = brightness == Brightness.dark; @@ -96,8 +100,11 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { // Components avatarTheme ??= const StreamAvatarThemeData(); + buttonTheme ??= const StreamButtonThemeData(); onlineIndicatorTheme ??= const StreamOnlineIndicatorThemeData(); + componentFactory ??= StreamComponentFactory(); + return .raw( brightness: brightness, radius: radius, @@ -107,7 +114,9 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { textTheme: textTheme, boxShadow: boxShadow, avatarTheme: avatarTheme, + buttonTheme: buttonTheme, onlineIndicatorTheme: onlineIndicatorTheme, + componentFactory: componentFactory, ); } @@ -132,7 +141,9 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { required this.textTheme, required this.boxShadow, required this.avatarTheme, + required this.buttonTheme, required this.onlineIndicatorTheme, + required this.componentFactory, }); /// Returns the [StreamTheme] from the closest [Theme] ancestor. @@ -193,6 +204,11 @@ class StreamTheme extends ThemeExtension with _$StreamTheme { /// The avatar theme for this theme. final StreamAvatarThemeData avatarTheme; + /// The button theme for this theme. + final StreamButtonThemeData buttonTheme; + /// The online indicator theme for this theme. final StreamOnlineIndicatorThemeData onlineIndicatorTheme; + + final StreamComponentFactory componentFactory; } 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 59918ca..dd2dae7 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 @@ -20,7 +20,9 @@ mixin _$StreamTheme on ThemeExtension { StreamTextTheme? textTheme, StreamBoxShadow? boxShadow, StreamAvatarThemeData? avatarTheme, + StreamButtonThemeData? buttonTheme, StreamOnlineIndicatorThemeData? onlineIndicatorTheme, + StreamComponentFactory? componentFactory, }) { final _this = (this as StreamTheme); @@ -33,7 +35,9 @@ mixin _$StreamTheme on ThemeExtension { textTheme: textTheme ?? _this.textTheme, boxShadow: boxShadow ?? _this.boxShadow, avatarTheme: avatarTheme ?? _this.avatarTheme, + buttonTheme: buttonTheme ?? _this.buttonTheme, onlineIndicatorTheme: onlineIndicatorTheme ?? _this.onlineIndicatorTheme, + componentFactory: componentFactory ?? _this.componentFactory, ); } @@ -62,11 +66,15 @@ mixin _$StreamTheme on ThemeExtension { other.avatarTheme, t, )!, + buttonTheme: t < 0.5 ? _this.buttonTheme : other.buttonTheme, onlineIndicatorTheme: StreamOnlineIndicatorThemeData.lerp( _this.onlineIndicatorTheme, other.onlineIndicatorTheme, t, )!, + componentFactory: t < 0.5 + ? _this.componentFactory + : other.componentFactory, ); } @@ -91,7 +99,9 @@ mixin _$StreamTheme on ThemeExtension { _other.textTheme == _this.textTheme && _other.boxShadow == _this.boxShadow && _other.avatarTheme == _this.avatarTheme && - _other.onlineIndicatorTheme == _this.onlineIndicatorTheme; + _other.buttonTheme == _this.buttonTheme && + _other.onlineIndicatorTheme == _this.onlineIndicatorTheme && + _other.componentFactory == _this.componentFactory; } @override @@ -108,7 +118,9 @@ mixin _$StreamTheme on ThemeExtension { _this.textTheme, _this.boxShadow, _this.avatarTheme, + _this.buttonTheme, _this.onlineIndicatorTheme, + _this.componentFactory, ); } } 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 ce24083..d104890 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 @@ -1,6 +1,7 @@ import 'package:flutter/widgets.dart'; import 'components/stream_avatar_theme.dart'; +import 'components/stream_button_theme.dart'; import 'components/stream_online_indicator_theme.dart'; import 'primitives/stream_radius.dart'; import 'primitives/stream_spacing.dart'; @@ -55,6 +56,9 @@ extension StreamThemeExtension on BuildContext { /// Returns the [StreamAvatarThemeData] from the nearest ancestor. StreamAvatarThemeData get streamAvatarTheme => StreamAvatarTheme.of(this); + /// Returns the [StreamButtonThemeData] from the nearest ancestor. + StreamButtonThemeData get streamButtonTheme => StreamButtonTheme.of(this); + /// Returns the [StreamOnlineIndicatorThemeData] from the nearest ancestor. StreamOnlineIndicatorThemeData get streamOnlineIndicatorTheme => StreamOnlineIndicatorTheme.of(this); } From 7f2162e3ae99f32e46c55c5a49e29d1348e4d176 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Tue, 27 Jan 2026 17:09:38 +0100 Subject: [PATCH 02/12] add todo --- .../lib/src/components/buttons/stream_button.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart index d5874fb..0d3c317 100644 --- a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart +++ b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart @@ -91,6 +91,8 @@ class DefaultStreamButton extends StatelessWidget { final buttonTheme = context.streamButtonTheme; final defaults = _StreamButtonDefaults(context: context); + // TODO: disabled styles + final themeColors = switch (props.style) { StreamButtonStyle.primary => buttonTheme.primaryButtonColors, StreamButtonStyle.secondary => buttonTheme.secondaryButtonColors, From 3496f9749965f4464465aa610b1fbf314f369202 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Wed, 28 Jan 2026 10:48:19 +0100 Subject: [PATCH 03/12] add colors for disabled button --- .../src/components/buttons/stream_button.dart | 24 ++++++---- .../theme/components/stream_button_theme.dart | 6 +++ .../stream_button_theme.g.theme.dart | 45 ++++++++++++++++++- .../theme/semantics/stream_color_scheme.dart | 10 +++++ .../stream_color_scheme.g.theme.dart | 10 +++++ 5 files changed, 86 insertions(+), 9 deletions(-) diff --git a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart index 0d3c317..b235903 100644 --- a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart +++ b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart @@ -91,22 +91,22 @@ class DefaultStreamButton extends StatelessWidget { final buttonTheme = context.streamButtonTheme; final defaults = _StreamButtonDefaults(context: context); - // TODO: disabled styles + final isDisabled = props.onTap == null; final themeColors = switch (props.style) { - StreamButtonStyle.primary => buttonTheme.primaryButtonColors, - StreamButtonStyle.secondary => buttonTheme.secondaryButtonColors, - StreamButtonStyle.destructive => buttonTheme.destructiveButtonColors, + StreamButtonStyle.primary => isDisabled ? buttonTheme.disabledPrimaryButtonColors : buttonTheme.primaryButtonColors, + StreamButtonStyle.secondary => isDisabled ? buttonTheme.disabledSecondaryButtonColors : buttonTheme.secondaryButtonColors, + StreamButtonStyle.destructive => isDisabled ? buttonTheme.disabledDestructiveButtonColors : buttonTheme.destructiveButtonColors, }; final defaultColors = switch (props.style) { - StreamButtonStyle.primary => defaults.primaryColors, - StreamButtonStyle.secondary => defaults.secondaryColors, - StreamButtonStyle.destructive => defaults.destructiveColors, + StreamButtonStyle.primary => isDisabled ? defaults.disabledColors : defaults.primaryColors, + StreamButtonStyle.secondary => isDisabled ? defaults.disabledColors : defaults.secondaryColors, + StreamButtonStyle.destructive => isDisabled ? defaults.disabledColors : defaults.destructiveColors, }; final backgroundColor = switch (props.type) { - StreamButtonType.solid => themeColors?.solidBackgroundColor ?? defaultColors.solidBackgroundColor, + StreamButtonType.solid => themeColors?.solidBackgroundColor ?? defaultColors.solidBackgroundColor, StreamButtonType.outline => Colors.transparent, StreamButtonType.ghost => Colors.transparent, }; @@ -195,4 +195,12 @@ class _StreamButtonDefaults { outlineForegroundColor: _colorScheme.textOnAccent, ghostForegroundColor: _colorScheme.textOnAccent, ); + + StreamButtonColors get disabledColors => StreamButtonColors( + solidBackgroundColor: _colorScheme.backgroundDisabled, + solidForegroundColor: _colorScheme.textDisabled, + outlineBorderColor: _colorScheme.borderDefault, + outlineForegroundColor: _colorScheme.textDisabled, + ghostForegroundColor: _colorScheme.textDisabled, + ); } diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart index 5098a03..ec888b7 100644 --- a/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart @@ -33,13 +33,19 @@ class StreamButtonTheme extends InheritedTheme { class StreamButtonThemeData with _$StreamButtonThemeData { const StreamButtonThemeData({ this.primaryButtonColors, + this.disabledPrimaryButtonColors, this.secondaryButtonColors, + this.disabledSecondaryButtonColors, this.destructiveButtonColors, + this.disabledDestructiveButtonColors, }); final StreamButtonColors? primaryButtonColors; + final StreamButtonColors? disabledPrimaryButtonColors; final StreamButtonColors? secondaryButtonColors; + final StreamButtonColors? disabledSecondaryButtonColors; final StreamButtonColors? destructiveButtonColors; + final StreamButtonColors? disabledDestructiveButtonColors; } @themeGen diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart index b79fa83..494e731 100644 --- a/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart @@ -33,28 +33,47 @@ mixin _$StreamButtonThemeData { primaryButtonColors: t < 0.5 ? a.primaryButtonColors : b.primaryButtonColors, + disabledPrimaryButtonColors: t < 0.5 + ? a.disabledPrimaryButtonColors + : b.disabledPrimaryButtonColors, secondaryButtonColors: t < 0.5 ? a.secondaryButtonColors : b.secondaryButtonColors, + disabledSecondaryButtonColors: t < 0.5 + ? a.disabledSecondaryButtonColors + : b.disabledSecondaryButtonColors, destructiveButtonColors: t < 0.5 ? a.destructiveButtonColors : b.destructiveButtonColors, + disabledDestructiveButtonColors: t < 0.5 + ? a.disabledDestructiveButtonColors + : b.disabledDestructiveButtonColors, ); } StreamButtonThemeData copyWith({ StreamButtonColors? primaryButtonColors, + StreamButtonColors? disabledPrimaryButtonColors, StreamButtonColors? secondaryButtonColors, + StreamButtonColors? disabledSecondaryButtonColors, StreamButtonColors? destructiveButtonColors, + StreamButtonColors? disabledDestructiveButtonColors, }) { final _this = (this as StreamButtonThemeData); return StreamButtonThemeData( primaryButtonColors: primaryButtonColors ?? _this.primaryButtonColors, + disabledPrimaryButtonColors: + disabledPrimaryButtonColors ?? _this.disabledPrimaryButtonColors, secondaryButtonColors: secondaryButtonColors ?? _this.secondaryButtonColors, + disabledSecondaryButtonColors: + disabledSecondaryButtonColors ?? _this.disabledSecondaryButtonColors, destructiveButtonColors: destructiveButtonColors ?? _this.destructiveButtonColors, + disabledDestructiveButtonColors: + disabledDestructiveButtonColors ?? + _this.disabledDestructiveButtonColors, ); } @@ -73,12 +92,27 @@ mixin _$StreamButtonThemeData { primaryButtonColors: _this.primaryButtonColors?.merge(other.primaryButtonColors) ?? other.primaryButtonColors, + disabledPrimaryButtonColors: + _this.disabledPrimaryButtonColors?.merge( + other.disabledPrimaryButtonColors, + ) ?? + other.disabledPrimaryButtonColors, secondaryButtonColors: _this.secondaryButtonColors?.merge(other.secondaryButtonColors) ?? other.secondaryButtonColors, + disabledSecondaryButtonColors: + _this.disabledSecondaryButtonColors?.merge( + other.disabledSecondaryButtonColors, + ) ?? + other.disabledSecondaryButtonColors, destructiveButtonColors: _this.destructiveButtonColors?.merge(other.destructiveButtonColors) ?? other.destructiveButtonColors, + disabledDestructiveButtonColors: + _this.disabledDestructiveButtonColors?.merge( + other.disabledDestructiveButtonColors, + ) ?? + other.disabledDestructiveButtonColors, ); } @@ -96,8 +130,14 @@ mixin _$StreamButtonThemeData { final _other = (other as StreamButtonThemeData); return _other.primaryButtonColors == _this.primaryButtonColors && + _other.disabledPrimaryButtonColors == + _this.disabledPrimaryButtonColors && _other.secondaryButtonColors == _this.secondaryButtonColors && - _other.destructiveButtonColors == _this.destructiveButtonColors; + _other.disabledSecondaryButtonColors == + _this.disabledSecondaryButtonColors && + _other.destructiveButtonColors == _this.destructiveButtonColors && + _other.disabledDestructiveButtonColors == + _this.disabledDestructiveButtonColors; } @override @@ -107,8 +147,11 @@ mixin _$StreamButtonThemeData { return Object.hash( runtimeType, _this.primaryButtonColors, + _this.disabledPrimaryButtonColors, _this.secondaryButtonColors, + _this.disabledSecondaryButtonColors, _this.destructiveButtonColors, + _this.disabledDestructiveButtonColors, ); } } diff --git a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart index 0642452..d80548a 100644 --- a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart +++ b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart @@ -66,6 +66,7 @@ class StreamColorScheme with _$StreamColorScheme { Color? backgroundSurfaceSubtle, Color? backgroundSurfaceStrong, Color? backgroundOverlay, + Color? backgroundDisabled, // Border - Core Color? borderSurface, Color? borderSurfaceSubtle, @@ -119,6 +120,7 @@ class StreamColorScheme with _$StreamColorScheme { backgroundSurfaceSubtle ??= StreamColors.slate.shade100; backgroundSurfaceStrong ??= StreamColors.slate.shade200; backgroundOverlay ??= StreamColors.black10; + backgroundDisabled ??= StreamColors.slate.shade100; // Border - Core borderSurface ??= StreamColors.slate.shade400; @@ -192,6 +194,7 @@ class StreamColorScheme with _$StreamColorScheme { backgroundSurfaceSubtle: backgroundSurfaceSubtle, backgroundSurfaceStrong: backgroundSurfaceStrong, backgroundOverlay: backgroundOverlay, + backgroundDisabled: backgroundDisabled, borderDefault: borderDefault, borderSurface: borderSurface, borderSurfaceSubtle: borderSurfaceSubtle, @@ -241,6 +244,7 @@ class StreamColorScheme with _$StreamColorScheme { Color? backgroundSurfaceSubtle, Color? backgroundSurfaceStrong, Color? backgroundOverlay, + Color? backgroundDisabled, // Border - Core Color? borderDefault, Color? borderSurface, @@ -294,6 +298,7 @@ class StreamColorScheme with _$StreamColorScheme { backgroundSurfaceSubtle ??= StreamColors.neutral.shade800; backgroundSurfaceStrong ??= StreamColors.neutral.shade700; backgroundOverlay ??= StreamColors.black50; + backgroundDisabled ??= StreamColors.neutral.shade900; // Border - Core borderDefault ??= StreamColors.neutral.shade800; @@ -367,6 +372,7 @@ class StreamColorScheme with _$StreamColorScheme { backgroundSurfaceSubtle: backgroundSurfaceSubtle, backgroundSurfaceStrong: backgroundSurfaceStrong, backgroundOverlay: backgroundOverlay, + backgroundDisabled: backgroundDisabled, borderDefault: borderDefault, borderSurface: borderSurface, borderSurfaceSubtle: borderSurfaceSubtle, @@ -414,6 +420,7 @@ class StreamColorScheme with _$StreamColorScheme { required this.backgroundSurfaceSubtle, required this.backgroundSurfaceStrong, required this.backgroundOverlay, + required this.backgroundDisabled, // Border - Core required this.borderDefault, required this.borderSurface, @@ -505,6 +512,9 @@ class StreamColorScheme with _$StreamColorScheme { /// The overlay background color. final Color backgroundOverlay; + /// The disabled background color. + final Color backgroundDisabled; + // ---- Border colors - Core ---- /// The default border color. diff --git a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.g.theme.dart b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.g.theme.dart index eaf4c2b..926c312 100644 --- a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.g.theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.g.theme.dart @@ -64,6 +64,11 @@ mixin _$StreamColorScheme { b.backgroundOverlay, t, )!, + backgroundDisabled: Color.lerp( + a.backgroundDisabled, + b.backgroundDisabled, + t, + )!, borderDefault: Color.lerp(a.borderDefault, b.borderDefault, t)!, borderSurface: Color.lerp(a.borderSurface, b.borderSurface, t)!, borderSurfaceSubtle: Color.lerp( @@ -116,6 +121,7 @@ mixin _$StreamColorScheme { Color? backgroundSurfaceSubtle, Color? backgroundSurfaceStrong, Color? backgroundOverlay, + Color? backgroundDisabled, Color? borderDefault, Color? borderSurface, Color? borderSurfaceSubtle, @@ -162,6 +168,7 @@ mixin _$StreamColorScheme { backgroundSurfaceStrong: backgroundSurfaceStrong ?? _this.backgroundSurfaceStrong, backgroundOverlay: backgroundOverlay ?? _this.backgroundOverlay, + backgroundDisabled: backgroundDisabled ?? _this.backgroundDisabled, borderDefault: borderDefault ?? _this.borderDefault, borderSurface: borderSurface ?? _this.borderSurface, borderSurfaceSubtle: borderSurfaceSubtle ?? _this.borderSurfaceSubtle, @@ -217,6 +224,7 @@ mixin _$StreamColorScheme { backgroundSurfaceSubtle: other.backgroundSurfaceSubtle, backgroundSurfaceStrong: other.backgroundSurfaceStrong, backgroundOverlay: other.backgroundOverlay, + backgroundDisabled: other.backgroundDisabled, borderDefault: other.borderDefault, borderSurface: other.borderSurface, borderSurfaceSubtle: other.borderSurfaceSubtle, @@ -273,6 +281,7 @@ mixin _$StreamColorScheme { _other.backgroundSurfaceSubtle == _this.backgroundSurfaceSubtle && _other.backgroundSurfaceStrong == _this.backgroundSurfaceStrong && _other.backgroundOverlay == _this.backgroundOverlay && + _other.backgroundDisabled == _this.backgroundDisabled && _other.borderDefault == _this.borderDefault && _other.borderSurface == _this.borderSurface && _other.borderSurfaceSubtle == _this.borderSurfaceSubtle && @@ -321,6 +330,7 @@ mixin _$StreamColorScheme { _this.backgroundSurfaceSubtle, _this.backgroundSurfaceStrong, _this.backgroundOverlay, + _this.backgroundDisabled, _this.borderDefault, _this.borderSurface, _this.borderSurfaceSubtle, From 9b9644c9218d90002b8a836b4ba74c2648209e91 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Wed, 28 Jan 2026 12:28:06 +0100 Subject: [PATCH 04/12] use tokens for border --- .../lib/src/theme/semantics/stream_color_scheme.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart index d80548a..c493d39 100644 --- a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart +++ b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart @@ -123,6 +123,7 @@ class StreamColorScheme with _$StreamColorScheme { backgroundDisabled ??= StreamColors.slate.shade100; // Border - Core + borderDefault ??= light_tokens.StreamTokens.borderCoreDefault; borderSurface ??= StreamColors.slate.shade400; borderSurfaceSubtle ??= StreamColors.slate.shade200; borderSurfaceStrong ??= StreamColors.slate.shade600; @@ -132,7 +133,6 @@ class StreamColorScheme with _$StreamColorScheme { borderImage ??= StreamColors.black10; // Border - Utility - borderDefault ??= StreamColors.slate.shade150; borderFocus ??= brand.shade300; borderDisabled ??= StreamColors.slate.shade100; borderError ??= accentError; @@ -301,7 +301,7 @@ class StreamColorScheme with _$StreamColorScheme { backgroundDisabled ??= StreamColors.neutral.shade900; // Border - Core - borderDefault ??= StreamColors.neutral.shade800; + borderDefault ??= dark_tokens.StreamTokens.borderCoreDefault; borderSurface ??= StreamColors.neutral.shade500; borderSurfaceSubtle ??= StreamColors.neutral.shade700; borderSurfaceStrong ??= StreamColors.neutral.shade400; @@ -512,12 +512,12 @@ class StreamColorScheme with _$StreamColorScheme { /// The overlay background color. final Color backgroundOverlay; - /// The disabled background color. + /// Disabled background for inputs, buttons, or chips. final Color backgroundDisabled; // ---- Border colors - Core ---- - /// The default border color. + /// Standard surface border final Color borderDefault; /// The standard surface border color. From e7f829baa62b2cb5099bc1160d94807e25a253d5 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Wed, 28 Jan 2026 13:54:01 +0100 Subject: [PATCH 05/12] format --- .../lib/src/components/buttons/stream_button.dart | 11 +++++++---- .../lib/src/theme/semantics/stream_color_scheme.dart | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart index b235903..ca59bb5 100644 --- a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart +++ b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart @@ -94,9 +94,12 @@ class DefaultStreamButton extends StatelessWidget { final isDisabled = props.onTap == null; final themeColors = switch (props.style) { - StreamButtonStyle.primary => isDisabled ? buttonTheme.disabledPrimaryButtonColors : buttonTheme.primaryButtonColors, - StreamButtonStyle.secondary => isDisabled ? buttonTheme.disabledSecondaryButtonColors : buttonTheme.secondaryButtonColors, - StreamButtonStyle.destructive => isDisabled ? buttonTheme.disabledDestructiveButtonColors : buttonTheme.destructiveButtonColors, + StreamButtonStyle.primary => + isDisabled ? buttonTheme.disabledPrimaryButtonColors : buttonTheme.primaryButtonColors, + StreamButtonStyle.secondary => + isDisabled ? buttonTheme.disabledSecondaryButtonColors : buttonTheme.secondaryButtonColors, + StreamButtonStyle.destructive => + isDisabled ? buttonTheme.disabledDestructiveButtonColors : buttonTheme.destructiveButtonColors, }; final defaultColors = switch (props.style) { @@ -106,7 +109,7 @@ class DefaultStreamButton extends StatelessWidget { }; final backgroundColor = switch (props.type) { - StreamButtonType.solid => themeColors?.solidBackgroundColor ?? defaultColors.solidBackgroundColor, + StreamButtonType.solid => themeColors?.solidBackgroundColor ?? defaultColors.solidBackgroundColor, StreamButtonType.outline => Colors.transparent, StreamButtonType.ghost => Colors.transparent, }; diff --git a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart index c493d39..1ff1ea0 100644 --- a/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart +++ b/packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart @@ -68,6 +68,7 @@ class StreamColorScheme with _$StreamColorScheme { Color? backgroundOverlay, Color? backgroundDisabled, // Border - Core + Color? borderDefault, Color? borderSurface, Color? borderSurfaceSubtle, Color? borderSurfaceStrong, @@ -76,7 +77,6 @@ class StreamColorScheme with _$StreamColorScheme { Color? borderSubtle, Color? borderImage, // Border - Utility - Color? borderDefault, Color? borderFocus, Color? borderDisabled, Color? borderError, From 00fd829ba8045c3c8b24aa0774cfbfb797794b54 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Thu, 29 Jan 2026 10:08:53 +0100 Subject: [PATCH 06/12] update button theme with more layering --- .../src/components/buttons/stream_button.dart | 150 ++++++++------ .../theme/components/stream_button_theme.dart | 50 ++--- .../stream_button_theme.g.theme.dart | 192 +++++------------- 3 files changed, 167 insertions(+), 225 deletions(-) diff --git a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart index ca59bb5..ae396ee 100644 --- a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart +++ b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart @@ -91,40 +91,33 @@ class DefaultStreamButton extends StatelessWidget { final buttonTheme = context.streamButtonTheme; final defaults = _StreamButtonDefaults(context: context); - final isDisabled = props.onTap == null; - - final themeColors = switch (props.style) { - StreamButtonStyle.primary => - isDisabled ? buttonTheme.disabledPrimaryButtonColors : buttonTheme.primaryButtonColors, - StreamButtonStyle.secondary => - isDisabled ? buttonTheme.disabledSecondaryButtonColors : buttonTheme.secondaryButtonColors, - StreamButtonStyle.destructive => - isDisabled ? buttonTheme.disabledDestructiveButtonColors : buttonTheme.destructiveButtonColors, + final themeButtonTypeStyle = switch (props.style) { + StreamButtonStyle.primary => buttonTheme.primary, + StreamButtonStyle.secondary => buttonTheme.secondary, + StreamButtonStyle.destructive => buttonTheme.destructive, }; - final defaultColors = switch (props.style) { - StreamButtonStyle.primary => isDisabled ? defaults.disabledColors : defaults.primaryColors, - StreamButtonStyle.secondary => isDisabled ? defaults.disabledColors : defaults.secondaryColors, - StreamButtonStyle.destructive => isDisabled ? defaults.disabledColors : defaults.destructiveColors, + final themeStyle = switch (props.type) { + StreamButtonType.solid => themeButtonTypeStyle?.solid, + StreamButtonType.outline => themeButtonTypeStyle?.outline, + StreamButtonType.ghost => themeButtonTypeStyle?.ghost, }; - final backgroundColor = switch (props.type) { - StreamButtonType.solid => themeColors?.solidBackgroundColor ?? defaultColors.solidBackgroundColor, - StreamButtonType.outline => Colors.transparent, - StreamButtonType.ghost => Colors.transparent, + final defaultButtonTypeStyle = switch (props.style) { + StreamButtonStyle.primary => defaults.primary, + StreamButtonStyle.secondary => defaults.secondary, + StreamButtonStyle.destructive => defaults.destructive, }; - - final foregroundColor = switch (props.type) { - StreamButtonType.solid => themeColors?.solidForegroundColor ?? defaultColors.solidForegroundColor, - StreamButtonType.outline => themeColors?.outlineForegroundColor ?? defaultColors.outlineForegroundColor, - StreamButtonType.ghost => themeColors?.ghostForegroundColor ?? defaultColors.ghostForegroundColor, + final defaultStyle = switch (props.type) { + StreamButtonType.solid => defaultButtonTypeStyle.solid, + StreamButtonType.outline => defaultButtonTypeStyle.outline, + StreamButtonType.ghost => defaultButtonTypeStyle.ghost, }; - final borderColor = switch (props.type) { - StreamButtonType.solid => null, - StreamButtonType.outline => themeColors?.outlineBorderColor ?? defaultColors.outlineBorderColor, - StreamButtonType.ghost => null, - }; + final backgroundColor = + themeStyle?.backgroundColor ?? defaultStyle?.backgroundColor ?? WidgetStateProperty.all(Colors.transparent); + final foregroundColor = themeStyle?.foregroundColor ?? defaultStyle?.foregroundColor; + final borderColor = themeStyle?.borderColor ?? defaultStyle?.borderColor; final minimumSize = switch (props.size) { StreamButtonSize.small => 32.0, @@ -137,14 +130,14 @@ class DefaultStreamButton extends StatelessWidget { return ElevatedButton( onPressed: props.onTap, style: ButtonStyle( - backgroundColor: WidgetStateProperty.all(backgroundColor), - foregroundColor: WidgetStateProperty.all(foregroundColor), + backgroundColor: backgroundColor, + foregroundColor: foregroundColor, minimumSize: WidgetStateProperty.all(Size(minimumSize, minimumSize)), padding: WidgetStateProperty.all(EdgeInsets.symmetric(horizontal: spacing.md)), side: borderColor == null ? null - : WidgetStateProperty.all( - BorderSide(color: borderColor), + : WidgetStateProperty.resolveWith( + (states) => BorderSide(color: borderColor.resolve(states)), ), elevation: WidgetStateProperty.all(0), shape: props.label == null @@ -175,35 +168,76 @@ class _StreamButtonDefaults { final BuildContext context; final StreamColorScheme _colorScheme; - StreamButtonColors get primaryColors => StreamButtonColors( - solidBackgroundColor: _colorScheme.brand.shade500, - solidForegroundColor: _colorScheme.textOnAccent, - outlineBorderColor: _colorScheme.brand.shade200, - outlineForegroundColor: _colorScheme.brand.shade500, - ghostForegroundColor: _colorScheme.brand.shade500, - ); - - StreamButtonColors get secondaryColors => StreamButtonColors( - solidBackgroundColor: _colorScheme.backgroundSurface, - solidForegroundColor: _colorScheme.textPrimary, - outlineBorderColor: _colorScheme.borderDefault, - outlineForegroundColor: _colorScheme.textPrimary, - ghostForegroundColor: _colorScheme.textPrimary, + StreamButtonTypeStyle get primary => StreamButtonTypeStyle( + solid: StreamButtonThemeStyle( + backgroundColor: WidgetStateProperty.resolveWith( + (states) => + states.contains(WidgetState.disabled) ? _colorScheme.backgroundDisabled : _colorScheme.brand.shade500, + ), + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textOnAccent, + ), + ), + outline: StreamButtonThemeStyle( + borderColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.borderDefault : _colorScheme.brand.shade200, + ), + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.brand.shade500, + ), + ), + ghost: StreamButtonThemeStyle( + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.brand.shade500, + ), + ), ); - StreamButtonColors get destructiveColors => StreamButtonColors( - solidBackgroundColor: _colorScheme.accentError, - solidForegroundColor: _colorScheme.textOnAccent, - outlineBorderColor: _colorScheme.accentError, - outlineForegroundColor: _colorScheme.textOnAccent, - ghostForegroundColor: _colorScheme.textOnAccent, + StreamButtonTypeStyle get secondary => StreamButtonTypeStyle( + solid: StreamButtonThemeStyle( + backgroundColor: WidgetStateProperty.resolveWith( + (states) => + states.contains(WidgetState.disabled) ? _colorScheme.backgroundDisabled : _colorScheme.backgroundSurface, + ), + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textPrimary, + ), + ), + outline: StreamButtonThemeStyle( + borderColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.borderDefault : _colorScheme.brand.shade200, + ), + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.brand.shade500, + ), + ), + ghost: StreamButtonThemeStyle( + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textPrimary, + ), + ), ); - - StreamButtonColors get disabledColors => StreamButtonColors( - solidBackgroundColor: _colorScheme.backgroundDisabled, - solidForegroundColor: _colorScheme.textDisabled, - outlineBorderColor: _colorScheme.borderDefault, - outlineForegroundColor: _colorScheme.textDisabled, - ghostForegroundColor: _colorScheme.textDisabled, + StreamButtonTypeStyle get destructive => StreamButtonTypeStyle( + solid: StreamButtonThemeStyle( + backgroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.backgroundDisabled : _colorScheme.accentError, + ), + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textOnAccent, + ), + ), + outline: StreamButtonThemeStyle( + borderColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.borderDefault : _colorScheme.accentError, + ), + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textOnAccent, + ), + ), + ghost: StreamButtonThemeStyle( + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textOnAccent, + ), + ), ); } diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart index ec888b7..f586548 100644 --- a/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart @@ -32,36 +32,38 @@ class StreamButtonTheme extends InheritedTheme { @immutable class StreamButtonThemeData with _$StreamButtonThemeData { const StreamButtonThemeData({ - this.primaryButtonColors, - this.disabledPrimaryButtonColors, - this.secondaryButtonColors, - this.disabledSecondaryButtonColors, - this.destructiveButtonColors, - this.disabledDestructiveButtonColors, + this.primary, + this.secondary, + this.destructive, }); - final StreamButtonColors? primaryButtonColors; - final StreamButtonColors? disabledPrimaryButtonColors; - final StreamButtonColors? secondaryButtonColors; - final StreamButtonColors? disabledSecondaryButtonColors; - final StreamButtonColors? destructiveButtonColors; - final StreamButtonColors? disabledDestructiveButtonColors; + final StreamButtonTypeStyle? primary; + final StreamButtonTypeStyle? secondary; + final StreamButtonTypeStyle? destructive; } @themeGen @immutable -class StreamButtonColors with _$StreamButtonColors { - const StreamButtonColors({ - this.solidBackgroundColor, - this.solidForegroundColor, - this.outlineBorderColor, - this.outlineForegroundColor, - this.ghostForegroundColor, +class StreamButtonTypeStyle with _$StreamButtonTypeStyle { + const StreamButtonTypeStyle({ + this.solid, + this.outline, + this.ghost, }); - final Color? solidBackgroundColor; - final Color? solidForegroundColor; - final Color? outlineBorderColor; - final Color? outlineForegroundColor; - final Color? ghostForegroundColor; + final StreamButtonThemeStyle? solid; + final StreamButtonThemeStyle? outline; + final StreamButtonThemeStyle? ghost; +} + +class StreamButtonThemeStyle { + const StreamButtonThemeStyle({ + this.backgroundColor, + this.foregroundColor, + this.borderColor, + }); + + final WidgetStateProperty? backgroundColor; + final WidgetStateProperty? foregroundColor; + final WidgetStateProperty? borderColor; } diff --git a/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart index 494e731..5a36afd 100644 --- a/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart @@ -30,50 +30,23 @@ mixin _$StreamButtonThemeData { } return StreamButtonThemeData( - primaryButtonColors: t < 0.5 - ? a.primaryButtonColors - : b.primaryButtonColors, - disabledPrimaryButtonColors: t < 0.5 - ? a.disabledPrimaryButtonColors - : b.disabledPrimaryButtonColors, - secondaryButtonColors: t < 0.5 - ? a.secondaryButtonColors - : b.secondaryButtonColors, - disabledSecondaryButtonColors: t < 0.5 - ? a.disabledSecondaryButtonColors - : b.disabledSecondaryButtonColors, - destructiveButtonColors: t < 0.5 - ? a.destructiveButtonColors - : b.destructiveButtonColors, - disabledDestructiveButtonColors: t < 0.5 - ? a.disabledDestructiveButtonColors - : b.disabledDestructiveButtonColors, + primary: t < 0.5 ? a.primary : b.primary, + secondary: t < 0.5 ? a.secondary : b.secondary, + destructive: t < 0.5 ? a.destructive : b.destructive, ); } StreamButtonThemeData copyWith({ - StreamButtonColors? primaryButtonColors, - StreamButtonColors? disabledPrimaryButtonColors, - StreamButtonColors? secondaryButtonColors, - StreamButtonColors? disabledSecondaryButtonColors, - StreamButtonColors? destructiveButtonColors, - StreamButtonColors? disabledDestructiveButtonColors, + StreamButtonTypeStyle? primary, + StreamButtonTypeStyle? secondary, + StreamButtonTypeStyle? destructive, }) { final _this = (this as StreamButtonThemeData); return StreamButtonThemeData( - primaryButtonColors: primaryButtonColors ?? _this.primaryButtonColors, - disabledPrimaryButtonColors: - disabledPrimaryButtonColors ?? _this.disabledPrimaryButtonColors, - secondaryButtonColors: - secondaryButtonColors ?? _this.secondaryButtonColors, - disabledSecondaryButtonColors: - disabledSecondaryButtonColors ?? _this.disabledSecondaryButtonColors, - destructiveButtonColors: - destructiveButtonColors ?? _this.destructiveButtonColors, - disabledDestructiveButtonColors: - disabledDestructiveButtonColors ?? - _this.disabledDestructiveButtonColors, + primary: primary ?? _this.primary, + secondary: secondary ?? _this.secondary, + destructive: destructive ?? _this.destructive, ); } @@ -89,30 +62,10 @@ mixin _$StreamButtonThemeData { } return copyWith( - primaryButtonColors: - _this.primaryButtonColors?.merge(other.primaryButtonColors) ?? - other.primaryButtonColors, - disabledPrimaryButtonColors: - _this.disabledPrimaryButtonColors?.merge( - other.disabledPrimaryButtonColors, - ) ?? - other.disabledPrimaryButtonColors, - secondaryButtonColors: - _this.secondaryButtonColors?.merge(other.secondaryButtonColors) ?? - other.secondaryButtonColors, - disabledSecondaryButtonColors: - _this.disabledSecondaryButtonColors?.merge( - other.disabledSecondaryButtonColors, - ) ?? - other.disabledSecondaryButtonColors, - destructiveButtonColors: - _this.destructiveButtonColors?.merge(other.destructiveButtonColors) ?? - other.destructiveButtonColors, - disabledDestructiveButtonColors: - _this.disabledDestructiveButtonColors?.merge( - other.disabledDestructiveButtonColors, - ) ?? - other.disabledDestructiveButtonColors, + primary: _this.primary?.merge(other.primary) ?? other.primary, + secondary: _this.secondary?.merge(other.secondary) ?? other.secondary, + destructive: + _this.destructive?.merge(other.destructive) ?? other.destructive, ); } @@ -129,15 +82,9 @@ mixin _$StreamButtonThemeData { final _this = (this as StreamButtonThemeData); final _other = (other as StreamButtonThemeData); - return _other.primaryButtonColors == _this.primaryButtonColors && - _other.disabledPrimaryButtonColors == - _this.disabledPrimaryButtonColors && - _other.secondaryButtonColors == _this.secondaryButtonColors && - _other.disabledSecondaryButtonColors == - _this.disabledSecondaryButtonColors && - _other.destructiveButtonColors == _this.destructiveButtonColors && - _other.disabledDestructiveButtonColors == - _this.disabledDestructiveButtonColors; + return _other.primary == _this.primary && + _other.secondary == _this.secondary && + _other.destructive == _this.destructive; } @override @@ -146,22 +93,19 @@ mixin _$StreamButtonThemeData { return Object.hash( runtimeType, - _this.primaryButtonColors, - _this.disabledPrimaryButtonColors, - _this.secondaryButtonColors, - _this.disabledSecondaryButtonColors, - _this.destructiveButtonColors, - _this.disabledDestructiveButtonColors, + _this.primary, + _this.secondary, + _this.destructive, ); } } -mixin _$StreamButtonColors { +mixin _$StreamButtonTypeStyle { bool get canMerge => true; - static StreamButtonColors? lerp( - StreamButtonColors? a, - StreamButtonColors? b, + static StreamButtonTypeStyle? lerp( + StreamButtonTypeStyle? a, + StreamButtonTypeStyle? b, double t, ) { if (identical(a, b)) { @@ -176,56 +120,29 @@ mixin _$StreamButtonColors { return t == 0.0 ? a : null; } - return StreamButtonColors( - solidBackgroundColor: Color.lerp( - a.solidBackgroundColor, - b.solidBackgroundColor, - t, - ), - solidForegroundColor: Color.lerp( - a.solidForegroundColor, - b.solidForegroundColor, - t, - ), - outlineBorderColor: Color.lerp( - a.outlineBorderColor, - b.outlineBorderColor, - t, - ), - outlineForegroundColor: Color.lerp( - a.outlineForegroundColor, - b.outlineForegroundColor, - t, - ), - ghostForegroundColor: Color.lerp( - a.ghostForegroundColor, - b.ghostForegroundColor, - t, - ), + return StreamButtonTypeStyle( + solid: t < 0.5 ? a.solid : b.solid, + outline: t < 0.5 ? a.outline : b.outline, + ghost: t < 0.5 ? a.ghost : b.ghost, ); } - StreamButtonColors copyWith({ - Color? solidBackgroundColor, - Color? solidForegroundColor, - Color? outlineBorderColor, - Color? outlineForegroundColor, - Color? ghostForegroundColor, + StreamButtonTypeStyle copyWith({ + StreamButtonThemeStyle? solid, + StreamButtonThemeStyle? outline, + StreamButtonThemeStyle? ghost, }) { - final _this = (this as StreamButtonColors); - - return StreamButtonColors( - solidBackgroundColor: solidBackgroundColor ?? _this.solidBackgroundColor, - solidForegroundColor: solidForegroundColor ?? _this.solidForegroundColor, - outlineBorderColor: outlineBorderColor ?? _this.outlineBorderColor, - outlineForegroundColor: - outlineForegroundColor ?? _this.outlineForegroundColor, - ghostForegroundColor: ghostForegroundColor ?? _this.ghostForegroundColor, + final _this = (this as StreamButtonTypeStyle); + + return StreamButtonTypeStyle( + solid: solid ?? _this.solid, + outline: outline ?? _this.outline, + ghost: ghost ?? _this.ghost, ); } - StreamButtonColors merge(StreamButtonColors? other) { - final _this = (this as StreamButtonColors); + StreamButtonTypeStyle merge(StreamButtonTypeStyle? other) { + final _this = (this as StreamButtonTypeStyle); if (other == null || identical(_this, other)) { return _this; @@ -236,11 +153,9 @@ mixin _$StreamButtonColors { } return copyWith( - solidBackgroundColor: other.solidBackgroundColor, - solidForegroundColor: other.solidForegroundColor, - outlineBorderColor: other.outlineBorderColor, - outlineForegroundColor: other.outlineForegroundColor, - ghostForegroundColor: other.ghostForegroundColor, + solid: other.solid, + outline: other.outline, + ghost: other.ghost, ); } @@ -254,27 +169,18 @@ mixin _$StreamButtonColors { return false; } - final _this = (this as StreamButtonColors); - final _other = (other as StreamButtonColors); + final _this = (this as StreamButtonTypeStyle); + final _other = (other as StreamButtonTypeStyle); - return _other.solidBackgroundColor == _this.solidBackgroundColor && - _other.solidForegroundColor == _this.solidForegroundColor && - _other.outlineBorderColor == _this.outlineBorderColor && - _other.outlineForegroundColor == _this.outlineForegroundColor && - _other.ghostForegroundColor == _this.ghostForegroundColor; + return _other.solid == _this.solid && + _other.outline == _this.outline && + _other.ghost == _this.ghost; } @override int get hashCode { - final _this = (this as StreamButtonColors); + final _this = (this as StreamButtonTypeStyle); - return Object.hash( - runtimeType, - _this.solidBackgroundColor, - _this.solidForegroundColor, - _this.outlineBorderColor, - _this.outlineForegroundColor, - _this.ghostForegroundColor, - ); + return Object.hash(runtimeType, _this.solid, _this.outline, _this.ghost); } } From a7ef8b361ff07352459854ea62ddbf1875cec30c Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Thu, 29 Jan 2026 10:41:45 +0100 Subject: [PATCH 07/12] add snapshot tests enabled lfs for snapshots --- .gitattributes | 1 + .../stream_core_flutter_workflow.yml | 2 +- melos.yaml | 11 ++ packages/stream_core_flutter/dart_test.yaml | 2 + .../src/components/buttons/stream_button.dart | 8 +- packages/stream_core_flutter/pubspec.yaml | 4 + .../goldens/ci/stream_button_dark_matrix.png | 3 + .../goldens/ci/stream_button_disabled.png | 3 + .../goldens/ci/stream_button_icon_only.png | 3 + .../goldens/ci/stream_button_light_matrix.png | 3 + .../goldens/ci/stream_button_with_icons.png | 3 + .../buttons/stream_button_golden_test.dart | 170 ++++++++++++++++++ .../test/flutter_test_config.dart | 18 ++ 13 files changed, 226 insertions(+), 5 deletions(-) create mode 100644 .gitattributes create mode 100644 packages/stream_core_flutter/dart_test.yaml create mode 100644 packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_dark_matrix.png create mode 100644 packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_disabled.png create mode 100644 packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_icon_only.png create mode 100644 packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_light_matrix.png create mode 100644 packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_with_icons.png create mode 100644 packages/stream_core_flutter/test/components/buttons/stream_button_golden_test.dart create mode 100644 packages/stream_core_flutter/test/flutter_test_config.dart diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b3fee88 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +**/goldens/ci/*.png filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/stream_core_flutter_workflow.yml b/.github/workflows/stream_core_flutter_workflow.yml index 8985e9a..eaf5161 100644 --- a/.github/workflows/stream_core_flutter_workflow.yml +++ b/.github/workflows/stream_core_flutter_workflow.yml @@ -54,7 +54,7 @@ jobs: ## Test and coverage reporting - name: "Flutter Test" - run: melos run test:all + run: melos run test:ci - name: "Collect Coverage" run: melos run coverage:ignore-file --no-select - name: "Upload Coverage" diff --git a/melos.yaml b/melos.yaml index 11994af..02f6f70 100644 --- a/melos.yaml +++ b/melos.yaml @@ -126,6 +126,10 @@ scripts: melos exec -c 1 --fail-fast --depends-on=flutter_localizations -- \ "flutter gen-l10n && dart format ." + test:ci: + run: melos run test:dart --no-select && melos run test:flutter_ci --no-select + description: Run all Dart & Flutter tests in this project. + test:all: run: melos run test:dart --no-select && melos run test:flutter --no-select description: Run all Dart & Flutter tests in this project. @@ -144,6 +148,13 @@ scripts: flutter: true dirExists: test + test:flutter_ci: + run: melos exec -c 4 --fail-fast -- "flutter test --dart-define=IS_CI=true --coverage" + description: Run Flutter tests on CI for a specific package in this project. + packageFilters: + flutter: true + dirExists: test + test:fixes: run: melos exec -c 4 --fail-fast -- "cd test_fixes && dart fix --compare-to-golden" description: Verify dart fixes for a specific package in this project. diff --git a/packages/stream_core_flutter/dart_test.yaml b/packages/stream_core_flutter/dart_test.yaml new file mode 100644 index 0000000..f01b955 --- /dev/null +++ b/packages/stream_core_flutter/dart_test.yaml @@ -0,0 +1,2 @@ +tags: + golden: diff --git a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart index ae396ee..3e2cd46 100644 --- a/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart +++ b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart @@ -205,10 +205,10 @@ class _StreamButtonDefaults { ), outline: StreamButtonThemeStyle( borderColor: WidgetStateProperty.resolveWith( - (states) => states.contains(WidgetState.disabled) ? _colorScheme.borderDefault : _colorScheme.brand.shade200, + (states) => states.contains(WidgetState.disabled) ? _colorScheme.borderDefault : _colorScheme.borderDefault, ), foregroundColor: WidgetStateProperty.resolveWith( - (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.brand.shade500, + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textPrimary, ), ), ghost: StreamButtonThemeStyle( @@ -231,12 +231,12 @@ class _StreamButtonDefaults { (states) => states.contains(WidgetState.disabled) ? _colorScheme.borderDefault : _colorScheme.accentError, ), foregroundColor: WidgetStateProperty.resolveWith( - (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textOnAccent, + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.accentError, ), ), ghost: StreamButtonThemeStyle( foregroundColor: WidgetStateProperty.resolveWith( - (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textOnAccent, + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.accentError, ), ), ); diff --git a/packages/stream_core_flutter/pubspec.yaml b/packages/stream_core_flutter/pubspec.yaml index fda1e3c..10ca449 100644 --- a/packages/stream_core_flutter/pubspec.yaml +++ b/packages/stream_core_flutter/pubspec.yaml @@ -15,7 +15,11 @@ dependencies: theme_extensions_builder_annotation: ^7.1.0 dev_dependencies: + alchemist: ^0.13.0 build_runner: ^2.10.5 flutter_test: sdk: flutter theme_extensions_builder: ^7.1.0 + +flutter: + uses-material-design: true diff --git a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_dark_matrix.png b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_dark_matrix.png new file mode 100644 index 0000000..698ea2c --- /dev/null +++ b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_dark_matrix.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:268ad3745546d46c651c69f3e93802acc41300888e2febc239858a50a7024b32 +size 29579 diff --git a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_disabled.png b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_disabled.png new file mode 100644 index 0000000..68d85bc --- /dev/null +++ b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_disabled.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b7aadfe4a85d0bec94f331f2d52507b6b303129c1f94022908bcc9aa2d5f00f +size 8156 diff --git a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_icon_only.png b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_icon_only.png new file mode 100644 index 0000000..ddf7fea --- /dev/null +++ b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_icon_only.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4556e557bf704b00555d7bcbcb0a54a30af8c2a0b0cc69b15fe9efcf4358399 +size 8395 diff --git a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_light_matrix.png b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_light_matrix.png new file mode 100644 index 0000000..969c2fe --- /dev/null +++ b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_light_matrix.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c6cdf699f4c48d2c6183a9ab9e9941e3cb832bd85813e875c705e33666fdf15 +size 27292 diff --git a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_with_icons.png b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_with_icons.png new file mode 100644 index 0000000..432bdc7 --- /dev/null +++ b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_with_icons.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:17ff2ee452aee789b475af1b581c2f33f45dd1b36c1fbbee64bd45390c180a08 +size 3392 diff --git a/packages/stream_core_flutter/test/components/buttons/stream_button_golden_test.dart b/packages/stream_core_flutter/test/components/buttons/stream_button_golden_test.dart new file mode 100644 index 0000000..2ad1f90 --- /dev/null +++ b/packages/stream_core_flutter/test/components/buttons/stream_button_golden_test.dart @@ -0,0 +1,170 @@ +// 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('StreamButton Golden Tests', () { + goldenTest( + 'renders light theme matrix', + fileName: 'stream_button_light_matrix', + builder: () => GoldenTestGroup( + scenarioConstraints: const BoxConstraints(maxWidth: 400), + children: [ + for (final style in StreamButtonStyle.values) + for (final type in StreamButtonType.values) + for (final size in StreamButtonSize.values) + GoldenTestScenario( + name: '${style.name}_${type.name}_${size.name}', + child: _buildButtonInTheme( + StreamButton( + label: 'Button', + onTap: () {}, + style: style, + type: type, + size: size, + ), + ), + ), + ], + ), + ); + + goldenTest( + 'renders dark theme matrix', + fileName: 'stream_button_dark_matrix', + builder: () => GoldenTestGroup( + scenarioConstraints: const BoxConstraints(maxWidth: 400), + children: [ + for (final style in StreamButtonStyle.values) + for (final type in StreamButtonType.values) + for (final size in StreamButtonSize.values) + GoldenTestScenario( + name: '${style.name}_${type.name}_${size.name}', + child: _buildButtonInTheme( + StreamButton( + label: 'Button', + onTap: () {}, + style: style, + type: type, + size: size, + ), + brightness: Brightness.dark, + ), + ), + ], + ), + ); + + goldenTest( + 'renders button with icons correctly', + fileName: 'stream_button_with_icons', + builder: () => GoldenTestGroup( + scenarioConstraints: const BoxConstraints(maxWidth: 300), + children: [ + GoldenTestScenario( + name: 'icon left', + child: _buildButtonInTheme( + StreamButton( + label: 'Button', + onTap: () {}, + iconLeft: Icons.add, + ), + ), + ), + GoldenTestScenario( + name: 'icon right', + child: _buildButtonInTheme( + StreamButton( + label: 'Button', + onTap: () {}, + iconRight: Icons.arrow_forward, + ), + ), + ), + GoldenTestScenario( + name: 'both icons', + child: _buildButtonInTheme( + StreamButton( + label: 'Button', + onTap: () {}, + iconLeft: Icons.add, + iconRight: Icons.arrow_forward, + ), + ), + ), + ], + ), + ); + + goldenTest( + 'renders icon only button correctly', + fileName: 'stream_button_icon_only', + builder: () => GoldenTestGroup( + scenarioConstraints: const BoxConstraints(maxWidth: 300), + children: [ + for (final style in StreamButtonStyle.values) + for (final type in StreamButtonType.values) + GoldenTestScenario( + name: '${style.name}_${type.name}', + child: _buildButtonInTheme( + StreamButton.icon( + onTap: () {}, + style: style, + type: type, + icon: Icons.add, + ), + ), + ), + ], + ), + ); + + goldenTest( + 'renders disabled button correctly', + fileName: 'stream_button_disabled', + builder: () => GoldenTestGroup( + scenarioConstraints: const BoxConstraints(maxWidth: 300), + children: [ + for (final style in StreamButtonStyle.values) + for (final type in StreamButtonType.values) + GoldenTestScenario( + name: '${style.name}_${type.name}', + child: _buildButtonInTheme( + StreamButton( + label: 'Disabled', + onTap: null, + style: style, + type: type, + ), + ), + ), + ], + ), + ); + }); +} + +Widget _buildButtonInTheme( + Widget button, { + 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: button, + ), + ), + ), + ); +} diff --git a/packages/stream_core_flutter/test/flutter_test_config.dart b/packages/stream_core_flutter/test/flutter_test_config.dart new file mode 100644 index 0000000..67750dd --- /dev/null +++ b/packages/stream_core_flutter/test/flutter_test_config.dart @@ -0,0 +1,18 @@ +// ignore_for_file: do_not_use_environment + +import 'dart:async'; + +import 'package:alchemist/alchemist.dart'; + +Future testExecutable(FutureOr Function() testMain) async { + final isCI = const String.fromEnvironment('IS_CI', defaultValue: 'false').toLowerCase() == 'true'; + + return AlchemistConfig.runWithConfig( + config: AlchemistConfig( + // Disable platform-specific goldens to ensure consistent results across + // different machines and CI environments. + platformGoldensConfig: PlatformGoldensConfig(enabled: !isCI), + ), + run: testMain, + ); +} From d5c6f7dee640670c6c64e42ee34bd3885244ae70 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Thu, 29 Jan 2026 13:34:51 +0100 Subject: [PATCH 08/12] update git checkout for workflow --- .github/workflows/stream_core_flutter_workflow.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stream_core_flutter_workflow.yml b/.github/workflows/stream_core_flutter_workflow.yml index eaf5161..24c2e56 100644 --- a/.github/workflows/stream_core_flutter_workflow.yml +++ b/.github/workflows/stream_core_flutter_workflow.yml @@ -25,9 +25,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Git Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 with: fetch-depth: 0 + lfs: 'true' - name: Install Flutter uses: subosito/flutter-action@v2 From e4d4773556d677216b596f5ddb7e3d9b4d3c55e5 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Thu, 29 Jan 2026 14:10:34 +0100 Subject: [PATCH 09/12] improve CI param --- .github/workflows/stream_core_flutter_workflow.yml | 4 +++- melos.yaml | 11 ----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/workflows/stream_core_flutter_workflow.yml b/.github/workflows/stream_core_flutter_workflow.yml index 24c2e56..f75d91a 100644 --- a/.github/workflows/stream_core_flutter_workflow.yml +++ b/.github/workflows/stream_core_flutter_workflow.yml @@ -23,6 +23,8 @@ jobs: analyze: timeout-minutes: 15 runs-on: ubuntu-latest + env: + IS_CI: 'true' steps: - name: Git Checkout uses: actions/checkout@v6 @@ -55,7 +57,7 @@ jobs: ## Test and coverage reporting - name: "Flutter Test" - run: melos run test:ci + run: melos run test:all - name: "Collect Coverage" run: melos run coverage:ignore-file --no-select - name: "Upload Coverage" diff --git a/melos.yaml b/melos.yaml index 02f6f70..11994af 100644 --- a/melos.yaml +++ b/melos.yaml @@ -126,10 +126,6 @@ scripts: melos exec -c 1 --fail-fast --depends-on=flutter_localizations -- \ "flutter gen-l10n && dart format ." - test:ci: - run: melos run test:dart --no-select && melos run test:flutter_ci --no-select - description: Run all Dart & Flutter tests in this project. - test:all: run: melos run test:dart --no-select && melos run test:flutter --no-select description: Run all Dart & Flutter tests in this project. @@ -148,13 +144,6 @@ scripts: flutter: true dirExists: test - test:flutter_ci: - run: melos exec -c 4 --fail-fast -- "flutter test --dart-define=IS_CI=true --coverage" - description: Run Flutter tests on CI for a specific package in this project. - packageFilters: - flutter: true - dirExists: test - test:fixes: run: melos exec -c 4 --fail-fast -- "cd test_fixes && dart fix --compare-to-golden" description: Verify dart fixes for a specific package in this project. From 78459f1eaf7ced039b521698307656dc0783786a Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Thu, 29 Jan 2026 14:11:44 +0100 Subject: [PATCH 10/12] add workflow to update goldens (#39) --- .github/workflows/update_goldens.yml | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/update_goldens.yml diff --git a/.github/workflows/update_goldens.yml b/.github/workflows/update_goldens.yml new file mode 100644 index 0000000..85b409d --- /dev/null +++ b/.github/workflows/update_goldens.yml @@ -0,0 +1,39 @@ +name: update_goldens + +on: workflow_dispatch + +jobs: + update_goldens: + runs-on: ubuntu-latest + steps: + - name: 📚 Checkout branch + uses: actions/checkout@v6 + with: + ssh-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} + lfs: 'true' + + - name: 🐦 Install Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: "3.x" + channel: stable + cache: true + cache-key: flutter-:os:-:channel:-:version:-:arch:-:hash:-${{ hashFiles('**/pubspec.lock') }} + + - name: 📦 Install Tools + run: flutter pub global activate melos + + - name: 🔧 Bootstrap Workspace + run: melos bootstrap --verbose + + - name: 🖼️ Update Goldens + continue-on-error: true + run: melos run update:goldens + + - name: 📤 Commit Changes + uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "chore: Update Goldens" + file_pattern: "**/test/**/goldens/*.png" + commit_user_name: "Stream SDK Bot" + commit_user_email: "60655709+Stream-SDK-Bot@users.noreply.github.com" \ No newline at end of file From 3726833718ea8f9787ea86df25427a3776968360 Mon Sep 17 00:00:00 2001 From: renefloor <15101411+renefloor@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:13:50 +0000 Subject: [PATCH 11/12] chore: Update Goldens --- .../buttons/goldens/ci/stream_button_dark_matrix.png | 4 ++-- .../components/buttons/goldens/ci/stream_button_disabled.png | 4 ++-- .../components/buttons/goldens/ci/stream_button_icon_only.png | 4 ++-- .../buttons/goldens/ci/stream_button_light_matrix.png | 4 ++-- .../buttons/goldens/ci/stream_button_with_icons.png | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_dark_matrix.png b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_dark_matrix.png index 698ea2c..811f995 100644 --- a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_dark_matrix.png +++ b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_dark_matrix.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:268ad3745546d46c651c69f3e93802acc41300888e2febc239858a50a7024b32 -size 29579 +oid sha256:d235e7f5151450443c8bf80114d73fed2708883c11788ae35ddda66aedd512f7 +size 29609 diff --git a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_disabled.png b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_disabled.png index 68d85bc..8ddd5f4 100644 --- a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_disabled.png +++ b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b7aadfe4a85d0bec94f331f2d52507b6b303129c1f94022908bcc9aa2d5f00f -size 8156 +oid sha256:fa6238db3df5dc58e6443dcd7e51dac695d2934d73c4bf31588343c1df87d778 +size 8202 diff --git a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_icon_only.png b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_icon_only.png index ddf7fea..ef057fc 100644 --- a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_icon_only.png +++ b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_icon_only.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4556e557bf704b00555d7bcbcb0a54a30af8c2a0b0cc69b15fe9efcf4358399 -size 8395 +oid sha256:2e427b7eefaa925f764b151d6651276e655dfbae9bf50ec68f85ca75bdf55715 +size 8433 diff --git a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_light_matrix.png b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_light_matrix.png index 969c2fe..6ac172c 100644 --- a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_light_matrix.png +++ b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_light_matrix.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c6cdf699f4c48d2c6183a9ab9e9941e3cb832bd85813e875c705e33666fdf15 -size 27292 +oid sha256:6da33039c73b9291b9f2030a7dee09d66dd33cb4b46c762da9baa534dc23de58 +size 27372 diff --git a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_with_icons.png b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_with_icons.png index 432bdc7..410286f 100644 --- a/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_with_icons.png +++ b/packages/stream_core_flutter/test/components/buttons/goldens/ci/stream_button_with_icons.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17ff2ee452aee789b475af1b581c2f33f45dd1b36c1fbbee64bd45390c180a08 -size 3392 +oid sha256:5b8bdd71a65ad1008e63292749f1c590a984e264967f72d0976a8c89c03461f2 +size 3414 From 8ee70bbc637a0ce494c8932665685ab4d4aecbf6 Mon Sep 17 00:00:00 2001 From: Rene Floor Date: Thu, 29 Jan 2026 14:15:06 +0100 Subject: [PATCH 12/12] disable CI snapshots for local tests --- .../stream_core_flutter/test/flutter_test_config.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/stream_core_flutter/test/flutter_test_config.dart b/packages/stream_core_flutter/test/flutter_test_config.dart index 67750dd..b689d05 100644 --- a/packages/stream_core_flutter/test/flutter_test_config.dart +++ b/packages/stream_core_flutter/test/flutter_test_config.dart @@ -1,17 +1,18 @@ -// ignore_for_file: do_not_use_environment - import 'dart:async'; +import 'dart:io'; import 'package:alchemist/alchemist.dart'; Future testExecutable(FutureOr Function() testMain) async { - final isCI = const String.fromEnvironment('IS_CI', defaultValue: 'false').toLowerCase() == 'true'; + final isRunningInCi = Platform.environment.containsKey('GITHUB_ACTIONS'); return AlchemistConfig.runWithConfig( config: AlchemistConfig( + // Enable golden tests for CI environments and disable them for local environments. + ciGoldensConfig: CiGoldensConfig(enabled: isRunningInCi), // Disable platform-specific goldens to ensure consistent results across // different machines and CI environments. - platformGoldensConfig: PlatformGoldensConfig(enabled: !isCI), + platformGoldensConfig: PlatformGoldensConfig(enabled: !isRunningInCi), ), run: testMain, );