Skip to content

Commit

Permalink
feat: VoicesButtons (#662)
Browse files Browse the repository at this point in the history
* feat: Introduce VoicesFilledButton, PrefixSuffixDecorator and ButtonsExample UI Kit page

* feat: VoicesOutlinedButton

* feat: VoicesTextButton, VoicesButtonAffixDecoration

* feat: VoicesTextButton variants

* fix: VoicesTextButton variant style build docs

* feat: VoicesIconButton

* fix: remove redundant TODO

* Update Example Title

* fix: update collection package version in melos.yaml

* fix: use ListView.separated for spacings in ButtonsExample

* fix: missing dots in docs

* feat: leading icon and suffix text button example

* fix: spell checking fix and extract buttons theme
  • Loading branch information
damian-molinski authored Aug 5, 2024
1 parent 28ba381 commit ef75746
Show file tree
Hide file tree
Showing 15 changed files with 687 additions and 69 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'package:catalyst_voices/widgets/common/affix_decorator.dart';
import 'package:flutter/material.dart';

/// Adding SizedBox.shrink because we want spacing on both sites.
///
/// See [AffixDecorator] for more context.
///
/// Note:
/// This widget should not be exported outside of this package.
class VoicesButtonAffixDecoration extends StatelessWidget {
final Widget? leading;
final Widget? trailing;
final Widget child;

const VoicesButtonAffixDecoration({
super.key,
this.leading,
this.trailing,
required this.child,
});

@override
Widget build(BuildContext context) {
return AffixDecorator(
prefix: leading ?? (trailing != null ? const SizedBox.shrink() : null),
suffix: trailing ?? (leading != null ? const SizedBox.shrink() : null),
child: child,
);
}
}
41 changes: 41 additions & 0 deletions catalyst_voices/lib/widgets/buttons/voices_filled_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:catalyst_voices/widgets/buttons/voices_button_affix_decoration.dart';
import 'package:flutter/material.dart';

/// A button that combines a `FilledButton` with optional leading and trailing
/// elements.
///
/// This widget provides a convenient way to add icons or other widgets before
/// or after the button's child.
class VoicesFilledButton extends StatelessWidget {
/// The callback function invoked when the button is pressed.
final VoidCallback? onTap;

/// The widget to be displayed before the button's main content.
final Widget? leading;

/// The widget to be displayed after the button's main content.
final Widget? trailing;

/// The main content of the button.
final Widget child;

const VoicesFilledButton({
super.key,
this.onTap,
this.leading,
this.trailing,
required this.child,
});

@override
Widget build(BuildContext context) {
return FilledButton(
onPressed: onTap,
child: VoicesButtonAffixDecoration(
leading: leading,
trailing: trailing,
child: child,
),
);
}
}
97 changes: 97 additions & 0 deletions catalyst_voices/lib/widgets/buttons/voices_icon_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:flutter/material.dart';

enum _Variant { standard, primary, filled, tonal, outlined }

class VoicesIconButton extends StatelessWidget {
/// The callback function invoked when the button is pressed.
final VoidCallback? onTap;

/// Icon widget for this button.
final Widget child;

final _Variant _variant;

const VoicesIconButton({
super.key,
this.onTap,
required this.child,
}) : _variant = _Variant.standard;

const VoicesIconButton.primary({
super.key,
this.onTap,
required this.child,
}) : _variant = _Variant.primary;

const VoicesIconButton.filled({
super.key,
this.onTap,
required this.child,
}) : _variant = _Variant.filled;

const VoicesIconButton.tonal({
super.key,
this.onTap,
required this.child,
}) : _variant = _Variant.tonal;

const VoicesIconButton.outlined({
super.key,
this.onTap,
required this.child,
}) : _variant = _Variant.outlined;

@override
Widget build(BuildContext context) {
return IconButton(
onPressed: onTap,
style: _buildVariantStyle(context),
icon: child,
);
}

/// Majority of configuration takes takes places in theme builder
/// so we need to override only new properties.
///
/// See `catalyst.dart` file in `brands` package.
ButtonStyle? _buildVariantStyle(BuildContext context) {
final themeData = Theme.of(context);
final colors = themeData.colors;
final colorScheme = themeData.colorScheme;

return switch (_variant) {
/// Default themeData configuration corresponds with this variant.
_Variant.standard => null,
_Variant.primary => IconButton.styleFrom(
foregroundColor: colors.iconsPrimary,
),
_Variant.filled => IconButton.styleFrom(
foregroundColor: colors.iconsBackground,
backgroundColor: colorScheme.primary,
disabledForegroundColor: colors.iconsDisabled,
disabledBackgroundColor: colors.onSurfaceNeutral012,
),
_Variant.tonal => IconButton.styleFrom(
foregroundColor: colors.iconsForeground,
backgroundColor: colors.onSurfacePrimary012,
disabledForegroundColor: colors.iconsDisabled,
disabledBackgroundColor: colors.onSurfaceNeutral012,
),
_Variant.outlined => IconButton.styleFrom(
foregroundColor: colors.iconsForeground,
backgroundColor: Colors.transparent,
).copyWith(
side: WidgetStateProperty.resolveWith(
(states) {
if (states.contains(WidgetState.disabled)) {
return BorderSide(color: colors.onSurfaceNeutral012!);
}

return BorderSide(color: colors.outlineBorderVariant!);
},
),
),
};
}
}
41 changes: 41 additions & 0 deletions catalyst_voices/lib/widgets/buttons/voices_outlined_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:catalyst_voices/widgets/buttons/voices_button_affix_decoration.dart';
import 'package:flutter/material.dart';

/// A button that combines a `OutlinedButton` with optional leading and trailing
/// elements.
///
/// This widget provides a convenient way to add icons or other widgets before
/// or after the button's child.
class VoicesOutlinedButton extends StatelessWidget {
/// The callback function invoked when the button is pressed.
final VoidCallback? onTap;

/// The widget to be displayed before the button's main content.
final Widget? leading;

/// The widget to be displayed after the button's main content.
final Widget? trailing;

/// The main content of the button.
final Widget child;

const VoicesOutlinedButton({
super.key,
this.onTap,
this.leading,
this.trailing,
required this.child,
});

@override
Widget build(BuildContext context) {
return OutlinedButton(
onPressed: onTap,
child: VoicesButtonAffixDecoration(
leading: leading,
trailing: trailing,
child: child,
),
);
}
}
80 changes: 80 additions & 0 deletions catalyst_voices/lib/widgets/buttons/voices_text_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'package:catalyst_voices/widgets/buttons/voices_button_affix_decoration.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:flutter/material.dart';

enum _Variant { primary, neutral, secondary }

/// A button that combines a `TextButton` with optional leading and trailing
/// elements.
///
/// This widget provides a convenient way to add icons or other widgets before
/// or after the button's child.
class VoicesTextButton extends StatelessWidget {
/// The callback function invoked when the button is pressed.
final VoidCallback? onTap;

/// The widget to be displayed before the button's main content.
final Widget? leading;

/// The widget to be displayed after the button's main content.
final Widget? trailing;

/// The main content of the button.
final Widget child;

final _Variant _variant;

const VoicesTextButton({
super.key,
this.onTap,
this.leading,
this.trailing,
required this.child,
}) : _variant = _Variant.primary;

const VoicesTextButton.neutral({
super.key,
this.onTap,
this.leading,
this.trailing,
required this.child,
}) : _variant = _Variant.neutral;

const VoicesTextButton.secondary({
super.key,
this.onTap,
this.leading,
this.trailing,
required this.child,
}) : _variant = _Variant.secondary;

@override
Widget build(BuildContext context) {
return TextButton(
onPressed: onTap,
style: _buildVariantStyle(context),
child: VoicesButtonAffixDecoration(
leading: leading,
trailing: trailing,
child: child,
),
);
}

/// Majority of configuration takes takes places in theme builder
/// so we need to override only new properties.
///
/// See `catalyst.dart` file in `brands` package.
ButtonStyle? _buildVariantStyle(BuildContext context) {
return switch (_variant) {
/// Default theme configuration corresponds with this variant
_Variant.primary => null,
_Variant.neutral => TextButton.styleFrom(
foregroundColor: Theme.of(context).colors.textPrimary,
),
_Variant.secondary => TextButton.styleFrom(
foregroundColor: Theme.of(context).colorScheme.secondary,
),
};
}
}
33 changes: 9 additions & 24 deletions catalyst_voices/lib/widgets/chips/voices_chip.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:catalyst_voices/widgets/common/affix_decorator.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -81,30 +82,14 @@ class VoicesChip extends StatelessWidget {
onTap: onTap,
child: Padding(
padding: padding,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (leading != null)
Padding(
padding: const EdgeInsetsDirectional.only(end: 8),
child: IconTheme(
data: iconTheme,
child: leading!,
),
),
DefaultTextStyle(
style: Theme.of(context).textTheme.labelLarge!,
child: content,
),
if (trailing != null)
Padding(
padding: const EdgeInsetsDirectional.only(start: 8),
child: IconTheme(
data: iconTheme,
child: trailing!,
),
),
],
child: AffixDecorator(
iconTheme: iconTheme,
prefix: leading,
suffix: trailing,
child: DefaultTextStyle(
style: Theme.of(context).textTheme.labelLarge!,
child: content,
),
),
),
),
Expand Down
Loading

0 comments on commit ef75746

Please sign in to comment.