diff --git a/lib/src/button/sbb_icon_text_button.dart b/lib/src/button/sbb_icon_text_button.dart index 96a17daf..a0037002 100644 --- a/lib/src/button/sbb_icon_text_button.dart +++ b/lib/src/button/sbb_icon_text_button.dart @@ -38,8 +38,14 @@ class _SBBIconTextButtonState extends State { @override Widget build(BuildContext context) { - final style = SBBButtonStyles.of(context).iconTextStyle; + final buttonStyle = SBBButtonStyles.of(context); + final style = buttonStyle.iconTextStyle; final isEnabled = widget.onPressed != null; + final textStyle = _isPressed || _hasFocus + ? style?.textStyleHighlighted + : isEnabled + ? style?.textStyle + : style?.textStyleDisabled; return Semantics( button: true, child: SBBGroup( @@ -83,15 +89,10 @@ class _SBBIconTextButtonState extends State { : style?.iconColorDisabled, ), const SizedBox(height: 4.0), - Text( + buttonStyle.buttonLabelBuilder( + context, widget.label, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: _isPressed || _hasFocus - ? style?.textStyleHighlighted - : isEnabled - ? style?.textStyle - : style?.textStyleDisabled, + style: textStyle, ), ], ), diff --git a/lib/src/button/sbb_primary_button.dart b/lib/src/button/sbb_primary_button.dart index f8bd5c63..6f91485e 100644 --- a/lib/src/button/sbb_primary_button.dart +++ b/lib/src/button/sbb_primary_button.dart @@ -18,15 +18,13 @@ import '../sbb_internal.dart'; class SBBPrimaryButton extends StatelessWidget { const SBBPrimaryButton({ super.key, - this.label, - this.child, + required this.label, this.isLoading = false, required this.onPressed, this.focusNode, - }) : assert(label != null && child == null || label == null && child != null); + }); - final String? label; - final Widget? child; + final String label; final bool isLoading; final VoidCallback? onPressed; final FocusNode? focusNode; @@ -44,29 +42,31 @@ class SBBPrimaryButton extends StatelessWidget { } ElevatedButton _buildThemedMobile(BuildContext context) { + final styles = SBBButtonStyles.of(context); return ElevatedButton( - style: SBBButtonStyles.of(context).primaryMobile, + style: styles.primaryMobile, onPressed: isLoading ? null : onPressed, focusNode: focusNode, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (isLoading) SBBLoadingIndicator.tinyCloud(), - if (label == null) child! else SBBButtonContent(label: label!), + styles.buttonLabelBuilder(context, label), ], ), ); } ElevatedButton _buildThemedWeb(BuildContext context) { + final styles = SBBButtonStyles.of(context); return ElevatedButton( - style: SBBButtonStyles.of(context).primaryWebLean, + style: styles.primaryWebLean, onPressed: onPressed, focusNode: focusNode, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (label == null) child! else SBBButtonContent(label: label!), + styles.buttonLabelBuilder(context, label), ], ), ); @@ -101,53 +101,42 @@ class SBBPrimaryButtonNegative extends StatelessWidget { @override Widget build(BuildContext context) { - final theme = Theme.of(context); switch (SBBBaseStyle.of(context).hostPlatform) { case HostPlatform.native: - return _buildThemedMobile(theme); + return _buildThemedMobile(context); case HostPlatform.web: - return _buildThemedWeb(theme); + return _buildThemedWeb(context); default: - return _buildThemedMobile(theme); + return _buildThemedMobile(context); } } - Widget _buildThemedMobile(ThemeData theme) { + Widget _buildThemedMobile(BuildContext context) { + final styles = SBBButtonStyles.of(context); return ElevatedButton( - style: theme.extension()?.primaryMobileNegative, + style: styles.primaryMobileNegative, onPressed: isLoading ? null : onPressed, focusNode: focusNode, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (isLoading) SBBLoadingIndicator.tinyCloud(), - Flexible( - child: Text( - label, - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - ), + styles.buttonLabelBuilder(context, label), ], ), ); } - Widget _buildThemedWeb(ThemeData theme) { + Widget _buildThemedWeb(BuildContext context) { + final styles = SBBButtonStyles.of(context); return ElevatedButton( - style: theme.extension()?.primaryWebNegative, + style: styles.primaryWebNegative, onPressed: onPressed, focusNode: focusNode, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Flexible( - child: Text( - label, - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - ), + styles.buttonLabelBuilder(context, label), ], ), ); diff --git a/lib/src/button/sbb_secondary_button.dart b/lib/src/button/sbb_secondary_button.dart index 2727978b..e5acfc0d 100644 --- a/lib/src/button/sbb_secondary_button.dart +++ b/lib/src/button/sbb_secondary_button.dart @@ -17,26 +17,25 @@ import '../sbb_internal.dart'; class SBBSecondaryButton extends StatelessWidget { const SBBSecondaryButton({ super. key, - this.label, - this.child, + required this.label, this.isLoading = false, required this.onPressed, this.focusNode, - }) : assert(label != null && child == null || label == null && child != null); + }); - final String? label; - final Widget? child; + final String label; final bool isLoading; final VoidCallback? onPressed; final FocusNode? focusNode; @override Widget build(BuildContext context) { - final isWeb = SBBBaseStyle.of(context).hostPlatform == HostPlatform.web; final style = SBBBaseStyle.of(context); + final isWeb = style.hostPlatform == HostPlatform.web; + final buttonStyles = SBBButtonStyles.of(context); return OutlinedButton( style: isWeb - ? Theme.of(context).extension()?.secondaryWebLean + ? buttonStyles.secondaryWebLean : Theme.of(context).outlinedButtonTheme.style?.copyWith( // workaround for web padding: SBBTheme.allStates(EdgeInsets.zero), @@ -54,7 +53,7 @@ class SBBSecondaryButton extends StatelessWidget { const SBBLoadingIndicator.tinySmoke(), const SBBLoadingIndicator.tinyCement(), ), - if (label == null) child! else SBBButtonContent(label: label!), + buttonStyles.buttonLabelBuilder(context, label), ], ), ), @@ -78,13 +77,16 @@ class SBBGhostButton extends StatelessWidget { Widget build(BuildContext context) { if (SBBBaseStyle.of(context).hostPlatform == HostPlatform.native) debugPrint('WARNING: Ghost button should only be used for web platform.'); + final styles = SBBButtonStyles.of(context); return OutlinedButton( - style: Theme.of(context).extension()?.ghostWebLean, + style: styles.ghostWebLean, onPressed: onPressed, focusNode: focusNode, child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [SBBButtonContent(label: label)], + children: [ + styles.buttonLabelBuilder(context, label), + ], ), ); } diff --git a/lib/src/button/sbb_tertiary_button.dart b/lib/src/button/sbb_tertiary_button.dart index f18cc7f1..46510982 100644 --- a/lib/src/button/sbb_tertiary_button.dart +++ b/lib/src/button/sbb_tertiary_button.dart @@ -17,16 +17,14 @@ import '../sbb_internal.dart'; class SBBTertiaryButtonLarge extends StatelessWidget { const SBBTertiaryButtonLarge({ super.key, - this.label, - this.child, + required this.label, this.icon, this.isLoading = false, required this.onPressed, this.focusNode, - }) : assert(label != null && child == null || label == null && child != null); + }); - final String? label; - final Widget? child; + final String label; final IconData? icon; final bool isLoading; final VoidCallback? onPressed; @@ -35,6 +33,7 @@ class SBBTertiaryButtonLarge extends StatelessWidget { @override Widget build(BuildContext context) { final style = SBBBaseStyle.of(context); + final buttonStyles = SBBButtonStyles.of(context); return TextButton( style: Theme.of(context).textButtonTheme.style?.copyWith( // workaround for web @@ -58,7 +57,7 @@ class SBBTertiaryButtonLarge extends StatelessWidget { padding: const EdgeInsetsDirectional.only(end: 4.0), child: Icon(icon, size: sbbIconSizeSmall), ), - if (label == null) child! else SBBButtonContent(label: label!), + buttonStyles.buttonLabelBuilder(context, label), ], ), ), @@ -124,11 +123,7 @@ class SBBTertiaryButtonSmall extends StatelessWidget { padding: const EdgeInsetsDirectional.only(end: 4.0), child: Icon(icon, size: sbbIconSizeSmall), ), - Text( - label, - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), + buttonStyle.buttonLabelBuilder(context, label), ], ), ), diff --git a/lib/src/sbb_internal.dart b/lib/src/sbb_internal.dart index 33b3b25f..c4d0a72d 100644 --- a/lib/src/sbb_internal.dart +++ b/lib/src/sbb_internal.dart @@ -41,9 +41,11 @@ class SBBInternal { } class SBBButtonContent extends StatelessWidget { - const SBBButtonContent({Key? key, required this.label}) : super(key: key); + const SBBButtonContent({Key? key, required this.label, this.style}) + : super(key: key); final String label; + final TextStyle? style; @override Widget build(BuildContext context) { @@ -52,6 +54,7 @@ class SBBButtonContent extends StatelessWidget { label, overflow: TextOverflow.ellipsis, maxLines: 1, + style: style, ), ); } diff --git a/lib/src/theme/styles/src/sbb_button_styles.dart b/lib/src/theme/styles/src/sbb_button_styles.dart index 88f38a51..b53114d7 100644 --- a/lib/src/theme/styles/src/sbb_button_styles.dart +++ b/lib/src/theme/styles/src/sbb_button_styles.dart @@ -3,6 +3,12 @@ import 'package:flutter/material.dart'; import '../../../sbb_internal.dart'; import '../sbb_styles.dart'; +typedef ButtonLabelBuilder = Widget Function( + BuildContext context, + String label, { + TextStyle? style, + }); + class SBBButtonStyles extends ThemeExtension { SBBButtonStyles({ this.primaryStyle, @@ -17,6 +23,7 @@ class SBBButtonStyles extends ThemeExtension { this.iconSmallBorderlessStyle, this.iconFormStyle, this.iconTextStyle, + required this.buttonLabelBuilder, }); factory SBBButtonStyles.$default({required SBBBaseStyle baseStyle}) => SBBButtonStyles( @@ -154,8 +161,13 @@ class SBBButtonStyles extends ThemeExtension { iconColorHighlighted: baseStyle.defaultTextColor, iconColorDisabled: SBBColors.metal, ), + buttonLabelBuilder: defaultButtonLabelBuilder, ); + static ButtonLabelBuilder defaultButtonLabelBuilder = (_, String label, { TextStyle? style }) { + return SBBButtonContent(label: label, style: style); + }; + final SBBButtonStyle? primaryStyle; final SBBButtonStyle? primaryNegativeStyle; final SBBButtonStyle? secondaryStyle; @@ -168,6 +180,7 @@ class SBBButtonStyles extends ThemeExtension { final SBBButtonStyle? iconSmallBorderlessStyle; final SBBButtonStyle? iconFormStyle; final SBBButtonStyle? iconTextStyle; + final ButtonLabelBuilder buttonLabelBuilder; static SBBButtonStyles of(BuildContext context) => Theme.of(context).extension()!; @@ -211,6 +224,7 @@ class SBBButtonStyles extends ThemeExtension { SBBButtonStyle? iconSmallBorderlessStyle, SBBButtonStyle? iconFormStyle, SBBButtonStyle? iconTextStyle, + ButtonLabelBuilder? buttonLabelBuilder, }) => SBBButtonStyles( primaryStyle: primaryStyle ?? this.primaryStyle, @@ -225,6 +239,7 @@ class SBBButtonStyles extends ThemeExtension { iconSmallBorderlessStyle: iconSmallBorderlessStyle ?? this.iconSmallBorderlessStyle, iconFormStyle: iconFormStyle ?? this.iconFormStyle, iconTextStyle: iconTextStyle ?? this.iconTextStyle, + buttonLabelBuilder: buttonLabelBuilder ?? this.buttonLabelBuilder, ); @override @@ -243,13 +258,16 @@ class SBBButtonStyles extends ThemeExtension { iconSmallBorderlessStyle: iconSmallBorderlessStyle?.lerp(other.iconSmallBorderlessStyle, t), iconFormStyle: iconFormStyle?.lerp(other.iconFormStyle, t), iconTextStyle: iconTextStyle?.lerp(other.iconTextStyle, t), + buttonLabelBuilder: other.buttonLabelBuilder, ); } } extension ButtonStylesExtension on SBBButtonStyles? { SBBButtonStyles merge(SBBButtonStyles? other) { - if (this == null) return other ?? SBBButtonStyles(); + if (this == null) return other ?? SBBButtonStyles( + buttonLabelBuilder: SBBButtonStyles.defaultButtonLabelBuilder, + ); return this!.copyWith( primaryStyle: this!.primaryStyle.merge(other?.primaryStyle), primaryNegativeStyle: this!.primaryNegativeStyle.merge(other?.primaryNegativeStyle), @@ -263,6 +281,7 @@ extension ButtonStylesExtension on SBBButtonStyles? { iconSmallBorderlessStyle: this!.iconSmallBorderlessStyle.merge(other?.iconSmallBorderlessStyle), iconFormStyle: this!.iconFormStyle.merge(other?.iconFormStyle), iconTextStyle: this!.iconTextStyle.merge(other?.iconTextStyle), + buttonLabelBuilder: other?.buttonLabelBuilder ?? this!.buttonLabelBuilder, ) as SBBButtonStyles; } }