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..f75d91a 100644 --- a/.github/workflows/stream_core_flutter_workflow.yml +++ b/.github/workflows/stream_core_flutter_workflow.yml @@ -23,11 +23,14 @@ jobs: analyze: timeout-minutes: 15 runs-on: ubuntu-latest + env: + IS_CI: 'true' 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 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 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/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.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..3e2cd46 --- /dev/null +++ b/packages/stream_core_flutter/lib/src/components/buttons/stream_button.dart @@ -0,0 +1,243 @@ +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 themeButtonTypeStyle = switch (props.style) { + StreamButtonStyle.primary => buttonTheme.primary, + StreamButtonStyle.secondary => buttonTheme.secondary, + StreamButtonStyle.destructive => buttonTheme.destructive, + }; + + final themeStyle = switch (props.type) { + StreamButtonType.solid => themeButtonTypeStyle?.solid, + StreamButtonType.outline => themeButtonTypeStyle?.outline, + StreamButtonType.ghost => themeButtonTypeStyle?.ghost, + }; + + final defaultButtonTypeStyle = switch (props.style) { + StreamButtonStyle.primary => defaults.primary, + StreamButtonStyle.secondary => defaults.secondary, + StreamButtonStyle.destructive => defaults.destructive, + }; + final defaultStyle = switch (props.type) { + StreamButtonType.solid => defaultButtonTypeStyle.solid, + StreamButtonType.outline => defaultButtonTypeStyle.outline, + StreamButtonType.ghost => defaultButtonTypeStyle.ghost, + }; + + 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, + StreamButtonSize.medium => 40.0, + StreamButtonSize.large => 48.0, + }; + + const iconSize = 20.0; + + return ElevatedButton( + onPressed: props.onTap, + style: ButtonStyle( + backgroundColor: backgroundColor, + foregroundColor: foregroundColor, + minimumSize: WidgetStateProperty.all(Size(minimumSize, minimumSize)), + padding: WidgetStateProperty.all(EdgeInsets.symmetric(horizontal: spacing.md)), + side: borderColor == null + ? null + : WidgetStateProperty.resolveWith( + (states) => BorderSide(color: borderColor.resolve(states)), + ), + 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; + + 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, + ), + ), + ); + + 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.borderDefault, + ), + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textPrimary, + ), + ), + ghost: StreamButtonThemeStyle( + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.textPrimary, + ), + ), + ); + 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.accentError, + ), + ), + ghost: StreamButtonThemeStyle( + foregroundColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.disabled) ? _colorScheme.textDisabled : _colorScheme.accentError, + ), + ), + ); +} 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..f586548 --- /dev/null +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.dart @@ -0,0 +1,69 @@ +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.primary, + this.secondary, + this.destructive, + }); + + final StreamButtonTypeStyle? primary; + final StreamButtonTypeStyle? secondary; + final StreamButtonTypeStyle? destructive; +} + +@themeGen +@immutable +class StreamButtonTypeStyle with _$StreamButtonTypeStyle { + const StreamButtonTypeStyle({ + this.solid, + this.outline, + this.ghost, + }); + + 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 new file mode 100644 index 0000000..5a36afd --- /dev/null +++ b/packages/stream_core_flutter/lib/src/theme/components/stream_button_theme.g.theme.dart @@ -0,0 +1,186 @@ +// 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( + 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({ + StreamButtonTypeStyle? primary, + StreamButtonTypeStyle? secondary, + StreamButtonTypeStyle? destructive, + }) { + final _this = (this as StreamButtonThemeData); + + return StreamButtonThemeData( + primary: primary ?? _this.primary, + secondary: secondary ?? _this.secondary, + destructive: destructive ?? _this.destructive, + ); + } + + StreamButtonThemeData merge(StreamButtonThemeData? other) { + final _this = (this as StreamButtonThemeData); + + if (other == null || identical(_this, other)) { + return _this; + } + + if (!other.canMerge) { + return other; + } + + return copyWith( + primary: _this.primary?.merge(other.primary) ?? other.primary, + secondary: _this.secondary?.merge(other.secondary) ?? other.secondary, + destructive: + _this.destructive?.merge(other.destructive) ?? other.destructive, + ); + } + + @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.primary == _this.primary && + _other.secondary == _this.secondary && + _other.destructive == _this.destructive; + } + + @override + int get hashCode { + final _this = (this as StreamButtonThemeData); + + return Object.hash( + runtimeType, + _this.primary, + _this.secondary, + _this.destructive, + ); + } +} + +mixin _$StreamButtonTypeStyle { + bool get canMerge => true; + + static StreamButtonTypeStyle? lerp( + StreamButtonTypeStyle? a, + StreamButtonTypeStyle? 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 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, + ); + } + + StreamButtonTypeStyle copyWith({ + StreamButtonThemeStyle? solid, + StreamButtonThemeStyle? outline, + StreamButtonThemeStyle? ghost, + }) { + final _this = (this as StreamButtonTypeStyle); + + return StreamButtonTypeStyle( + solid: solid ?? _this.solid, + outline: outline ?? _this.outline, + ghost: ghost ?? _this.ghost, + ); + } + + StreamButtonTypeStyle merge(StreamButtonTypeStyle? other) { + final _this = (this as StreamButtonTypeStyle); + + if (other == null || identical(_this, other)) { + return _this; + } + + if (!other.canMerge) { + return other; + } + + return copyWith( + solid: other.solid, + outline: other.outline, + ghost: other.ghost, + ); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + + if (other.runtimeType != runtimeType) { + return false; + } + + final _this = (this as StreamButtonTypeStyle); + final _other = (other as StreamButtonTypeStyle); + + return _other.solid == _this.solid && + _other.outline == _this.outline && + _other.ghost == _this.ghost; + } + + @override + int get hashCode { + final _this = (this as StreamButtonTypeStyle); + + return Object.hash(runtimeType, _this.solid, _this.outline, _this.ghost); + } +} 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..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 @@ -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'; @@ -64,7 +66,9 @@ class StreamColorScheme with _$StreamColorScheme { Color? backgroundSurfaceSubtle, Color? backgroundSurfaceStrong, Color? backgroundOverlay, + Color? backgroundDisabled, // Border - Core + Color? borderDefault, Color? borderSurface, Color? borderSurfaceSubtle, Color? borderSurfaceStrong, @@ -116,8 +120,10 @@ class StreamColorScheme with _$StreamColorScheme { backgroundSurfaceSubtle ??= StreamColors.slate.shade100; backgroundSurfaceStrong ??= StreamColors.slate.shade200; backgroundOverlay ??= StreamColors.black10; + backgroundDisabled ??= StreamColors.slate.shade100; // Border - Core + borderDefault ??= light_tokens.StreamTokens.borderCoreDefault; borderSurface ??= StreamColors.slate.shade400; borderSurfaceSubtle ??= StreamColors.slate.shade200; borderSurfaceStrong ??= StreamColors.slate.shade600; @@ -188,6 +194,8 @@ class StreamColorScheme with _$StreamColorScheme { backgroundSurfaceSubtle: backgroundSurfaceSubtle, backgroundSurfaceStrong: backgroundSurfaceStrong, backgroundOverlay: backgroundOverlay, + backgroundDisabled: backgroundDisabled, + borderDefault: borderDefault, borderSurface: borderSurface, borderSurfaceSubtle: borderSurfaceSubtle, borderSurfaceStrong: borderSurfaceStrong, @@ -236,7 +244,9 @@ class StreamColorScheme with _$StreamColorScheme { Color? backgroundSurfaceSubtle, Color? backgroundSurfaceStrong, Color? backgroundOverlay, + Color? backgroundDisabled, // Border - Core + Color? borderDefault, Color? borderSurface, Color? borderSurfaceSubtle, Color? borderSurfaceStrong, @@ -288,8 +298,10 @@ class StreamColorScheme with _$StreamColorScheme { backgroundSurfaceSubtle ??= StreamColors.neutral.shade800; backgroundSurfaceStrong ??= StreamColors.neutral.shade700; backgroundOverlay ??= StreamColors.black50; + backgroundDisabled ??= StreamColors.neutral.shade900; // Border - Core + borderDefault ??= dark_tokens.StreamTokens.borderCoreDefault; borderSurface ??= StreamColors.neutral.shade500; borderSurfaceSubtle ??= StreamColors.neutral.shade700; borderSurfaceStrong ??= StreamColors.neutral.shade400; @@ -360,6 +372,8 @@ class StreamColorScheme with _$StreamColorScheme { backgroundSurfaceSubtle: backgroundSurfaceSubtle, backgroundSurfaceStrong: backgroundSurfaceStrong, backgroundOverlay: backgroundOverlay, + backgroundDisabled: backgroundDisabled, + borderDefault: borderDefault, borderSurface: borderSurface, borderSurfaceSubtle: borderSurfaceSubtle, borderSurfaceStrong: borderSurfaceStrong, @@ -406,7 +420,9 @@ class StreamColorScheme with _$StreamColorScheme { required this.backgroundSurfaceSubtle, required this.backgroundSurfaceStrong, required this.backgroundOverlay, + required this.backgroundDisabled, // Border - Core + required this.borderDefault, required this.borderSurface, required this.borderSurfaceSubtle, required this.borderSurfaceStrong, @@ -496,8 +512,14 @@ class StreamColorScheme with _$StreamColorScheme { /// The overlay background color. final Color backgroundOverlay; + /// Disabled background for inputs, buttons, or chips. + final Color backgroundDisabled; + // ---- Border colors - Core ---- + /// Standard surface border + final Color borderDefault; + /// The standard surface border color. final Color borderSurface; @@ -610,21 +632,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 +657,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..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,12 @@ 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( a.borderSurfaceSubtle, @@ -115,6 +121,8 @@ mixin _$StreamColorScheme { Color? backgroundSurfaceSubtle, Color? backgroundSurfaceStrong, Color? backgroundOverlay, + Color? backgroundDisabled, + Color? borderDefault, Color? borderSurface, Color? borderSurfaceSubtle, Color? borderSurfaceStrong, @@ -160,6 +168,8 @@ 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, borderSurfaceStrong: borderSurfaceStrong ?? _this.borderSurfaceStrong, @@ -214,6 +224,8 @@ mixin _$StreamColorScheme { backgroundSurfaceSubtle: other.backgroundSurfaceSubtle, backgroundSurfaceStrong: other.backgroundSurfaceStrong, backgroundOverlay: other.backgroundOverlay, + backgroundDisabled: other.backgroundDisabled, + borderDefault: other.borderDefault, borderSurface: other.borderSurface, borderSurfaceSubtle: other.borderSurfaceSubtle, borderSurfaceStrong: other.borderSurfaceStrong, @@ -269,6 +281,8 @@ 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 && _other.borderSurfaceStrong == _this.borderSurfaceStrong && @@ -316,6 +330,8 @@ mixin _$StreamColorScheme { _this.backgroundSurfaceSubtle, _this.backgroundSurfaceStrong, _this.backgroundOverlay, + _this.backgroundDisabled, + _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); } 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..811f995 --- /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: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 new file mode 100644 index 0000000..8ddd5f4 --- /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: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 new file mode 100644 index 0000000..ef057fc --- /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: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 new file mode 100644 index 0000000..6ac172c --- /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: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 new file mode 100644 index 0000000..410286f --- /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:5b8bdd71a65ad1008e63292749f1c590a984e264967f72d0976a8c89c03461f2 +size 3414 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..b689d05 --- /dev/null +++ b/packages/stream_core_flutter/test/flutter_test_config.dart @@ -0,0 +1,19 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:alchemist/alchemist.dart'; + +Future testExecutable(FutureOr Function() testMain) async { + 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: !isRunningInCi), + ), + run: testMain, + ); +}