diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 95ab0d469..6e60e5d81 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -135,4 +135,7 @@ class Foo extends StatelessWidget { ## Conventions +* Avoid [double negatives](https://en.wikipedia.org/wiki/Double_negative) when naming things, i.e. a boolean field should + be named `enabled` instead of `disabled`. + * Prefix all publicly exported widgets and styles with `F`, i.e. `FScaffold`. diff --git a/forui/example/lib/main.dart b/forui/example/lib/main.dart index 00b472553..69684b381 100644 --- a/forui/example/lib/main.dart +++ b/forui/example/lib/main.dart @@ -102,6 +102,7 @@ class ExampleWidget extends StatelessWidget { FButton( text: 'Button', icon: FAssets.icons.airplay, + onPress: () {}, ) ], ); diff --git a/forui/lib/forui.dart b/forui/lib/forui.dart index 067d49cf7..035bf004d 100644 --- a/forui/lib/forui.dart +++ b/forui/lib/forui.dart @@ -15,7 +15,7 @@ export 'src/theme/themes.dart'; // Widgets export 'src/widgets/box.dart'; export 'src/widgets/badge/badge.dart' hide FBadgeContent; -export 'src/widgets/button/tappable.dart' hide FTappable; +export 'src/foundation/tappable.dart' hide FTappable; export 'src/widgets/button/button.dart' hide FButtonContent; export 'src/widgets/card/card.dart' hide FCardContent; export 'src/widgets/separator.dart'; diff --git a/forui/lib/src/widgets/button/tappable.dart b/forui/lib/src/foundation/tappable.dart similarity index 100% rename from forui/lib/src/widgets/button/tappable.dart rename to forui/lib/src/foundation/tappable.dart diff --git a/forui/lib/src/theme/color_scheme.dart b/forui/lib/src/theme/color_scheme.dart index d9eb9fa49..6bb9e06a9 100644 --- a/forui/lib/src/theme/color_scheme.dart +++ b/forui/lib/src/theme/color_scheme.dart @@ -1,7 +1,11 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -/// A set of colors that can be used to configure the color properties of most components. +import 'package:forui/forui.dart'; + +/// A set of colors that can be used to configure the colors of most widgets. +/// +/// See the pre-defined themes' color schemes in [FThemes]. final class FColorScheme with Diagnosticable { /// The background color. @@ -124,4 +128,5 @@ final class FColorScheme with Diagnosticable { destructive.hashCode ^ destructiveForeground.hashCode ^ border.hashCode; + } diff --git a/forui/lib/src/theme/font.dart b/forui/lib/src/theme/font.dart index a3304893b..2474a3c87 100644 --- a/forui/lib/src/theme/font.dart +++ b/forui/lib/src/theme/font.dart @@ -5,16 +5,17 @@ import 'package:sugar/core.dart'; import 'package:forui/forui.dart'; +// TODO: replace with nullable number operations in Sugar 4. double? _scale(double? value, double factor) => value == null ? null : value * factor; -/// A Forui font that used to configure the [TextStyle]s of Forui widgets. +/// A Forui font used to configure the Forui widgets' [TextStyle]s. /// -/// It is usually inherited from a ancestor [FTheme]. Besides the typical font information, a [FFont] also contains -/// scalar values used to scale a [TextStyle]'s corresponding properties. This ensures that various fonts are scaled -/// consistently throughout an application. +/// It is usually inherited from an ancestor [FTheme]. Besides the typical font information, a [FFont] also contains +/// scalar values used to scale a [TextStyle]'s corresponding properties. This ensures that various [TextStyle]s with the +/// same font are scaled consistently throughout a project. final class FFont with Diagnosticable { - /// The font family. Defaults to `packages/forui/Inter`. + /// The font family. Defaults to [`packages/forui/Inter`](https://fonts.google.com/specimen/Inter). /// /// ## Contract: /// Throws an [AssertionError] if blank. @@ -310,18 +311,18 @@ final class FFont with Diagnosticable { ..add(DoubleProperty('letterSpacingScalar', letterSpacingScalar, defaultValue: 1)) ..add(DoubleProperty('wordSpacingScalar', wordSpacingScalar, defaultValue: 1)) ..add(DoubleProperty('heightScalar', heightScalar, defaultValue: 1)) - ..add(DoubleProperty('xs', xs)) - ..add(DoubleProperty('sm', sm)) - ..add(DoubleProperty('base', base)) - ..add(DoubleProperty('lg', lg)) - ..add(DoubleProperty('xl', xl)) - ..add(DoubleProperty('xl2', xl2)) - ..add(DoubleProperty('xl3', xl3)) - ..add(DoubleProperty('xl4', xl4)) - ..add(DoubleProperty('xl5', xl5)) - ..add(DoubleProperty('xl6', xl6)) - ..add(DoubleProperty('xl7', xl7)) - ..add(DoubleProperty('xl8', xl8)); + ..add(DoubleProperty('xs', xs, defaultValue: 12)) + ..add(DoubleProperty('sm', sm, defaultValue: 14)) + ..add(DoubleProperty('base', base, defaultValue: 16)) + ..add(DoubleProperty('lg', lg, defaultValue: 18)) + ..add(DoubleProperty('xl', xl, defaultValue: 20)) + ..add(DoubleProperty('xl2', xl2, defaultValue: 22)) + ..add(DoubleProperty('xl3', xl3, defaultValue: 30)) + ..add(DoubleProperty('xl4', xl4, defaultValue: 36)) + ..add(DoubleProperty('xl5', xl5, defaultValue: 48)) + ..add(DoubleProperty('xl6', xl6, defaultValue: 60)) + ..add(DoubleProperty('xl7', xl7, defaultValue: 72)) + ..add(DoubleProperty('xl8', xl8, defaultValue: 96)); } @override @@ -371,7 +372,7 @@ final class FFont with Diagnosticable { /// Provides functions for working with [FFont]s. extension FontTextStyle on TextStyle { - /// Returns a [TextStyle] scaled using the given [font]. + /// Returns a [TextStyle] with the given [font], scaled using it. /// /// ```dart /// final font = FFont( diff --git a/forui/lib/src/theme/style.dart b/forui/lib/src/theme/style.dart index e601c92e6..3752fe1d9 100644 --- a/forui/lib/src/theme/style.dart +++ b/forui/lib/src/theme/style.dart @@ -1,7 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -/// The overarching style that is used to configure the properties of widget-specific styles if they are not provided. +/// The fallback global style is used to configure the widget-specific styles if they are not provided. final class FStyle with Diagnosticable { /// The border radius. diff --git a/forui/lib/src/theme/theme.dart b/forui/lib/src/theme/theme.dart index cbd5ab34c..c3a4b2a17 100644 --- a/forui/lib/src/theme/theme.dart +++ b/forui/lib/src/theme/theme.dart @@ -19,14 +19,14 @@ class FTheme extends StatelessWidget { /// The theme data. final FThemeData data; - /// The child widget. - final Widget child; - /// The text direction. /// /// If none is provided, the text direction is inherited from the context. final TextDirection? textDirection; + /// The child widget. + final Widget child; + /// Creates a [FTheme]. const FTheme({ required this.data, diff --git a/forui/lib/src/theme/theme_data.dart b/forui/lib/src/theme/theme_data.dart index 74dc102e1..503f1aae8 100644 --- a/forui/lib/src/theme/theme_data.dart +++ b/forui/lib/src/theme/theme_data.dart @@ -4,6 +4,7 @@ import 'package:forui/forui.dart'; /// The color scheme, fonts, overarching style, and widget specific styles used to configure child Forui widgets. class FThemeData with Diagnosticable { + /// The color scheme. final FColorScheme colorScheme; @@ -44,7 +45,7 @@ class FThemeData with Diagnosticable { required this.switchStyle, }); - /// Creates a [FThemeData] that inherits the given arguments' properties. + /// Creates a [FThemeData] that inherits the given properties. FThemeData.inherit({ required this.colorScheme, required this.font, @@ -52,7 +53,7 @@ class FThemeData with Diagnosticable { }): badgeStyles = FBadgeStyles.inherit(colorScheme: colorScheme, font: font, style: style), boxStyle = FBoxStyle.inherit(colorScheme: colorScheme), - buttonStyles = FButtonStyles.inherit(colorScheme: colorScheme, font: font, style: style, ), + buttonStyles = FButtonStyles.inherit(colorScheme: colorScheme, font: font, style: style), cardStyle = FCardStyle.inherit(colorScheme: colorScheme, font: font, style: style), separatorStyles = FSeparatorStyles.inherit(colorScheme: colorScheme, style: style), switchStyle = FSwitchStyle.inherit(colorScheme: colorScheme); @@ -121,4 +122,5 @@ class FThemeData with Diagnosticable { cardStyle.hashCode ^ separatorStyles.hashCode ^ switchStyle.hashCode; + } diff --git a/forui/lib/src/theme/themes.dart b/forui/lib/src/theme/themes.dart index 2d7d8a000..fa26643a2 100644 --- a/forui/lib/src/theme/themes.dart +++ b/forui/lib/src/theme/themes.dart @@ -42,4 +42,5 @@ extension FThemes on Never { style: FStyle(), ), ); + } diff --git a/forui/lib/src/widgets/badge/badge.dart b/forui/lib/src/widgets/badge/badge.dart index ac3786950..9c9045dd6 100644 --- a/forui/lib/src/widgets/badge/badge.dart +++ b/forui/lib/src/widgets/badge/badge.dart @@ -7,19 +7,13 @@ import 'package:meta/meta.dart'; import 'package:forui/forui.dart'; part 'badge_content.dart'; - part 'badge_styles.dart'; /// A badge, or a component that looks like a badge. class FBadge extends StatelessWidget { - /// The design. - final FBadgeDesign design; - - /// A callback for when the badge is pressed. - final void Function(BuildContext)? onPressed; - /// A callback for when the badge is long pressed. - final void Function(BuildContext)? onLongPressed; + /// The design. Defaults to [FBadgeVariant.primary]. + final FBadgeDesign design; /// The builder. final Widget Function(BuildContext, FBadgeStyle) builder; @@ -28,13 +22,11 @@ class FBadge extends StatelessWidget { FBadge({ required String label, this.design = FBadgeVariant.primary, - this.onPressed, - this.onLongPressed, super.key, }) : builder = ((context, style) => FBadgeContent(label: label, style: style)); /// Creates a [FBadge]. - const FBadge.raw({required this.design, required this.builder, this.onPressed, this.onLongPressed, super.key}); + const FBadge.raw({required this.design, required this.builder, super.key}); @override Widget build(BuildContext context) { @@ -46,7 +38,7 @@ class FBadge extends StatelessWidget { FBadgeVariant.destructive => context.theme.badgeStyles.destructive, }; - final badge = IntrinsicWidth( + return IntrinsicWidth( child: IntrinsicHeight( child: DecoratedBox( decoration: BoxDecoration( @@ -61,24 +53,16 @@ class FBadge extends StatelessWidget { ), ), ); - - if (onPressed == null && onLongPressed == null) { - return badge; - } - - // TODO: Wrap in FTappable when it's ready. - return badge; } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('design', design)) - ..add(DiagnosticsProperty('onPressed', onPressed, defaultValue: null)) - ..add(DiagnosticsProperty('onLongPressed', onLongPressed, defaultValue: null)) - ..add(DiagnosticsProperty('builder', builder, defaultValue: null)); + ..add(DiagnosticsProperty('design', design, defaultValue: FBadgeVariant.primary)) + ..add(DiagnosticsProperty('builder', builder, defaultValue: null)); } + } /// The badge design. Either a pre-defined [FBadgeVariant], or a custom [FBadgeStyle]. @@ -101,6 +85,7 @@ enum FBadgeVariant implements FBadgeDesign { /// A [FBadge]'s style. final class FBadgeStyle with Diagnosticable implements FBadgeDesign { + /// The background color. final Color background; @@ -161,7 +146,7 @@ final class FBadgeStyle with Diagnosticable implements FBadgeDesign { properties ..add(ColorProperty('background', background)) ..add(ColorProperty('border', border)) - ..add(DiagnosticsProperty('borderRadius', borderRadius)) + ..add(DiagnosticsProperty('borderRadius', borderRadius, defaultValue: BorderRadius.circular(100))) ..add(DoubleProperty('borderWidth', borderWidth)) ..add(DiagnosticsProperty('content', content)); } @@ -180,4 +165,5 @@ final class FBadgeStyle with Diagnosticable implements FBadgeDesign { @override int get hashCode => background.hashCode ^ border.hashCode ^ borderRadius.hashCode ^ borderWidth.hashCode ^ content.hashCode; + } diff --git a/forui/lib/src/widgets/badge/badge_content.dart b/forui/lib/src/widgets/badge/badge_content.dart index ab8f2f8fd..03e6a97f1 100644 --- a/forui/lib/src/widgets/badge/badge_content.dart +++ b/forui/lib/src/widgets/badge/badge_content.dart @@ -2,10 +2,10 @@ part of 'badge.dart'; @internal final class FBadgeContent extends StatelessWidget { - final String label; final FBadgeStyle style; + final String label; - const FBadgeContent({required this.label, required this.style, super.key}); + const FBadgeContent({required this.style, required this.label, super.key}); @override Widget build(BuildContext context) => Center( @@ -19,8 +19,8 @@ part of 'badge.dart'; void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('style', style)) - ..add(StringProperty('text', label)); + ..add(DiagnosticsProperty('style', style)) + ..add(StringProperty('label', label)); } } @@ -28,12 +28,12 @@ part of 'badge.dart'; /// A badge content's style. final class FBadgeContentStyle with Diagnosticable { - /// The padding. - final EdgeInsets padding; - /// The text. final TextStyle label; + /// The padding. + final EdgeInsets padding; + /// Creates a [FBadgeContentStyle]. FBadgeContentStyle({required this.label, this.padding = const EdgeInsets.symmetric(horizontal: 14, vertical: 2)}); @@ -47,8 +47,8 @@ final class FBadgeContentStyle with Diagnosticable { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('padding', padding)) - ..add(DiagnosticsProperty('text', label)); + ..add(DiagnosticsProperty('label', label)) + ..add(DiagnosticsProperty('padding', padding, defaultValue: const EdgeInsets.symmetric(horizontal: 14, vertical: 2))); } @override diff --git a/forui/lib/src/widgets/button/button.dart b/forui/lib/src/widgets/button/button.dart index 6038718eb..23f629ad2 100644 --- a/forui/lib/src/widgets/button/button.dart +++ b/forui/lib/src/widgets/button/button.dart @@ -5,8 +5,8 @@ import 'package:meta/meta.dart'; import 'package:nitrogen_types/nitrogen_types.dart'; import 'package:forui/forui.dart'; +import 'package:forui/src/foundation/tappable.dart'; import 'package:forui/src/svg_extension.nitrogen.dart'; -import 'package:forui/src/widgets/button/tappable.dart'; part 'button_content.dart'; @@ -18,34 +18,70 @@ part 'button_content_style.dart'; /// A button. class FButton extends StatelessWidget { - /// The style. + + /// The design. Defaults to [FBadgeVariant.primary]. final FButtonDesign design; - /// Called when the FButton is tapped or otherwise activated. - final VoidCallback? onPressed; + /// A callback for when the button is pressed. + final VoidCallback? onPress; + + /// A callback for when the button is long pressed. + final VoidCallback? onLongPress; + + /// True if this widget will be selected as the initial focus when no other node in its scope is currently focused. + /// + /// Ideally, there is only one widget with autofocus set in each FocusScope. If there is more than one widget with + /// autofocus set, then the first one added to the tree will get focus. + /// + /// Defaults to false. + final bool autofocus; + + /// An optional focus node to use as the focus node for this widget. + /// + /// If one is not supplied, then one will be automatically allocated, owned, and managed by this widget. The widget + /// will be focusable even if a [focusNode] is not supplied. If supplied, the given `focusNode` will be hosted by this + /// widget, but not owned. See [FocusNode] for more information on what being hosted and/or owned implies. + /// + /// Supplying a focus node is sometimes useful if an ancestor to this widget wants to control when this widget has the + /// focus. The owner will be responsible for calling [FocusNode.dispose] on the focus node when it is done with it, + /// but this widget will attach/detach and reparent the node when needed. + final FocusNode? focusNode; + + /// Handler called when the focus changes. + /// + /// Called with true if this widget's node gains focus, and false if it loses focus. + final ValueChanged? onFocusChange; /// The builder. final Widget Function(BuildContext, FButtonStyle) builder; - /// Creates a [FButton] widget. + /// Creates a [FButton]. FButton({ + required this.onPress, this.design = FButtonVariant.primary, - this.onPressed, + this.onLongPress, + this.autofocus = false, + this.focusNode, + this.onFocusChange, String? text, SvgAsset? icon, super.key, }) : builder = ((context, style) => FButtonContent( - text: text, - icon: icon, - style: style, - enabled: onPressed != null, - )); + text: text, + icon: icon, + style: style, + enabled: onPress != null, + )); /// Creates a [FButton]. const FButton.raw({ required this.design, + required this.onPress, required this.builder, - required this.onPressed, + this.onLongPress, + this.autofocus = false, + this.focusNode, + this.onFocusChange, super.key, }); @@ -58,11 +94,18 @@ class FButton extends StatelessWidget { FButtonVariant.outlined => context.theme.buttonStyles.outlined, FButtonVariant.destructive => context.theme.buttonStyles.destructive, }; - return FTappable( - onTap: onPressed, - child: DecoratedBox( - decoration: onPressed == null ? style.disabledBoxDecoration : style.enabledBoxDecoration, - child: builder(context, style), + + return FocusableActionDetector( + autofocus: autofocus, + focusNode: focusNode, + onFocusChange: onFocusChange, + child: FTappable( + onTap: onPress, + onLongPress: onLongPress, + child: DecoratedBox( + decoration: onPress == null ? style.disabledBoxDecoration : style.enabledBoxDecoration, + child: builder(context, style), + ), ), ); } @@ -71,8 +114,13 @@ class FButton extends StatelessWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('onPressed', onPressed)) - ..add(DiagnosticsProperty('style', design)) - ..add(ObjectFlagProperty.has('builder', builder)); + ..add(DiagnosticsProperty('design', design)) + ..add(DiagnosticsProperty('onPress', onPress)) + ..add(DiagnosticsProperty('onLongPress', onLongPress)) + ..add(FlagProperty('autofocus', value: autofocus, defaultValue: 'autofocus')) + ..add(DiagnosticsProperty('focusNode', focusNode)) + ..add(DiagnosticsProperty('onFocusChange', onFocusChange)) + ..add(DiagnosticsProperty('builder', builder)); } + } diff --git a/forui/lib/src/widgets/button/button_content.dart b/forui/lib/src/widgets/button/button_content.dart index fe0636dc4..cb52243ba 100644 --- a/forui/lib/src/widgets/button/button_content.dart +++ b/forui/lib/src/widgets/button/button_content.dart @@ -1,24 +1,13 @@ part of 'button.dart'; -/// Represents a content for [FButton]. -@internal -final class FButtonContent extends StatelessWidget { - /// The style. - final FButtonStyle style; +@internal final class FButtonContent extends StatelessWidget { - /// Whether the button should be enabled. + final FButtonStyle style; final bool enabled; - - /// The label on the button. final String? text; - - /// The icon; final SvgAsset? icon; - - /// The child. final Widget? child; - /// Creates a [FButtonContent]. const FButtonContent({ required this.style, this.enabled = true, @@ -31,14 +20,16 @@ final class FButtonContent extends StatelessWidget { @override Widget build(BuildContext context) { final style = this.style.content; - return Padding( padding: style.padding, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (icon != null) ...[ - icon!(height: 20, colorFilter: enabled ? style.enabledIcon : style.disabledIcon), + icon!( + height: 20, + colorFilter: ColorFilter.mode(enabled ? style.enabledIcon : style.disabledIcon, BlendMode.srcIn), + ), const SizedBox(width: 10) ], if (text != null) Flexible(child: Text(text!, style: enabled ? style.enabledText : style.disabledText)), @@ -52,9 +43,10 @@ final class FButtonContent extends StatelessWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(FlagProperty('enabled', value: enabled)) - ..add(DiagnosticsProperty('icon', icon)) + ..add(DiagnosticsProperty('style', style)) + ..add(FlagProperty('enabled', value: enabled, defaultValue: true)) ..add(StringProperty('text', text)) - ..add(DiagnosticsProperty('style', style)); + ..add(DiagnosticsProperty('icon', icon)); } + } diff --git a/forui/lib/src/widgets/button/button_content_style.dart b/forui/lib/src/widgets/button/button_content_style.dart index 4a2256f11..c7568c8ad 100644 --- a/forui/lib/src/widgets/button/button_content_style.dart +++ b/forui/lib/src/widgets/button/button_content_style.dart @@ -2,17 +2,18 @@ part of 'button.dart'; /// [FButtonContent]'s style. class FButtonContentStyle with Diagnosticable { - /// The TextStyle for an enabled button. + + /// The [TextStyle] when this button is enabled. final TextStyle enabledText; - /// The TextStyle for an disabled button. + /// The [TextStyle] when this button is disabled. final TextStyle disabledText; - /// The ColorFilter for an enabled button. - final ColorFilter enabledIcon; + /// The icon's color when this button is enabled. + final Color enabledIcon; - /// The ColorFilter for an disabled button. - final ColorFilter disabledIcon; + /// The icon's color when this button is disabled. + final Color disabledIcon; /// The padding. final EdgeInsets padding; @@ -26,7 +27,7 @@ class FButtonContentStyle with Diagnosticable { required this.padding, }); - /// Creates a [FButtonContentStyle] that inherits its properties from the given [foreground] and []. + /// Creates a [FButtonContentStyle] that inherits its properties from the given [foreground] and [disabledForeground]. FButtonContentStyle.inherit({ required FFont font,required Color foreground, required Color disabledForeground}) : padding = const EdgeInsets.symmetric( horizontal: 5, @@ -42,15 +43,18 @@ class FButtonContentStyle with Diagnosticable { fontWeight: FontWeight.w600, color: disabledForeground, ), - enabledIcon = ColorFilter.mode(foreground, BlendMode.srcIn), - disabledIcon = ColorFilter.mode(disabledForeground, BlendMode.srcIn); + enabledIcon = foreground, + disabledIcon = disabledForeground; + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties..add(DiagnosticsProperty('enabledText', enabledText)) - ..add(DiagnosticsProperty('disabledText', disabledText)) - ..add(DiagnosticsProperty('enabledIcon', enabledIcon)) - ..add(DiagnosticsProperty('disabledIcon', disabledIcon)) - ..add(DiagnosticsProperty('padding', padding)); + properties + ..add(DiagnosticsProperty('enabledText', enabledText)) + ..add(DiagnosticsProperty('disabledText', disabledText)) + ..add(DiagnosticsProperty('enabledIcon', enabledIcon)) + ..add(DiagnosticsProperty('disabledIcon', disabledIcon)) + ..add(DiagnosticsProperty('padding', padding)); } + } diff --git a/forui/lib/src/widgets/button/button_style.dart b/forui/lib/src/widgets/button/button_style.dart index 8c1f212f4..988509db1 100644 --- a/forui/lib/src/widgets/button/button_style.dart +++ b/forui/lib/src/widgets/button/button_style.dart @@ -20,6 +20,7 @@ enum FButtonVariant implements FButtonDesign { /// Represents the theme data that is inherited by [FButtonStyle] and used by child [FButton]. class FButtonStyle extends FButtonDesign with Diagnosticable{ + /// The content. final FButtonContentStyle content; @@ -47,11 +48,13 @@ class FButtonStyle extends FButtonDesign with Diagnosticable{ enabledBoxDecoration: enabledBoxDecoration ?? this.enabledBoxDecoration, disabledBoxDecoration: disabledBoxDecoration ?? this.disabledBoxDecoration, ); + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties..add(DiagnosticsProperty('content', content)) - ..add(DiagnosticsProperty('enabledBoxDecoration', enabledBoxDecoration)) - ..add(DiagnosticsProperty('disabledBoxDecoration', disabledBoxDecoration)); + properties..add(DiagnosticsProperty('content', content)) + ..add(DiagnosticsProperty('enabledBoxDecoration', enabledBoxDecoration)) + ..add(DiagnosticsProperty('disabledBoxDecoration', disabledBoxDecoration)); } + } diff --git a/forui/lib/src/widgets/button/button_styles.dart b/forui/lib/src/widgets/button/button_styles.dart index f20489563..be27d0a7e 100644 --- a/forui/lib/src/widgets/button/button_styles.dart +++ b/forui/lib/src/widgets/button/button_styles.dart @@ -34,9 +34,10 @@ class FButtonStyles with Diagnosticable{ color: colorScheme.primary.withOpacity(0.5), ), content: FButtonContentStyle.inherit( - font: font, - foreground: colorScheme.primaryForeground, - disabledForeground: colorScheme.primaryForeground.withOpacity(0.5)), + font: font, + foreground: colorScheme.primaryForeground, + disabledForeground: colorScheme.primaryForeground.withOpacity(0.5), + ), ), secondary = FButtonStyle( enabledBoxDecoration: BoxDecoration( @@ -48,9 +49,10 @@ class FButtonStyles with Diagnosticable{ color: colorScheme.secondary.withOpacity(0.5), ), content: FButtonContentStyle.inherit( - font: font, - foreground: colorScheme.secondaryForeground, - disabledForeground: colorScheme.secondaryForeground.withOpacity(0.5)), + font: font, + foreground: colorScheme.secondaryForeground, + disabledForeground: colorScheme.secondaryForeground.withOpacity(0.5), + ), ), destructive = FButtonStyle( enabledBoxDecoration: BoxDecoration( @@ -62,9 +64,10 @@ class FButtonStyles with Diagnosticable{ color: colorScheme.destructive.withOpacity(0.5), ), content: FButtonContentStyle.inherit( - font: font, - foreground: colorScheme.destructiveForeground, - disabledForeground: colorScheme.destructiveForeground.withOpacity(0.5)), + font: font, + foreground: colorScheme.destructiveForeground, + disabledForeground: colorScheme.destructiveForeground.withOpacity(0.5), + ), ), outlined = FButtonStyle( enabledBoxDecoration: BoxDecoration( @@ -80,16 +83,20 @@ class FButtonStyles with Diagnosticable{ color: colorScheme.background, ), content: FButtonContentStyle.inherit( - font: font, - foreground: colorScheme.secondaryForeground, - disabledForeground: colorScheme.secondaryForeground.withOpacity(0.5)), + font: font, + foreground: colorScheme.secondaryForeground, + disabledForeground: colorScheme.secondaryForeground.withOpacity(0.5), + ), ); + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties..add(DiagnosticsProperty('primary', primary)) - ..add(DiagnosticsProperty('secondary', secondary)) - ..add(DiagnosticsProperty('destructive', destructive)) - ..add(DiagnosticsProperty('outlined', outlined)); + properties + ..add(DiagnosticsProperty('primary', primary)) + ..add(DiagnosticsProperty('secondary', secondary)) + ..add(DiagnosticsProperty('destructive', destructive)) + ..add(DiagnosticsProperty('outlined', outlined)); } + } diff --git a/forui/lib/src/widgets/card/card.dart b/forui/lib/src/widgets/card/card.dart index aa37e76df..2a7b74394 100644 --- a/forui/lib/src/widgets/card/card.dart +++ b/forui/lib/src/widgets/card/card.dart @@ -9,12 +9,13 @@ part 'card_content.dart'; /// A card widget. final class FCard extends StatelessWidget { - /// The child. - final Widget child; /// The style. final FCardStyle? style; + /// The child. + final Widget child; + /// Creates a [FCard] with a tile and subtitle. FCard({ String? title, @@ -22,7 +23,12 @@ final class FCard extends StatelessWidget { Widget? child, this.style, super.key, - }) : child = FCardContent(title: title, subtitle: subtitle, style: style?.content, child: child,); + }) : child = FCardContent( + title: title, + subtitle: subtitle, + style: style?.content, + child: child, + ); /// Creates a [FCard]. const FCard.raw({required this.child, this.style, super.key}); @@ -40,12 +46,14 @@ final class FCard extends StatelessWidget { @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('style', style)); + properties.add(DiagnosticsProperty('style', style)); } + } /// [FCard]'s style. final class FCardStyle with Diagnosticable { + /// The decoration. final BoxDecoration decoration; @@ -74,8 +82,8 @@ final class FCardStyle with Diagnosticable { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('content', content)) - ..add(DiagnosticsProperty('decoration', decoration)); + ..add(DiagnosticsProperty('content', content)) + ..add(DiagnosticsProperty('decoration', decoration)); } @override @@ -86,4 +94,5 @@ final class FCardStyle with Diagnosticable { @override int get hashCode => decoration.hashCode ^ content.hashCode; + } diff --git a/forui/lib/src/widgets/card/card_content.dart b/forui/lib/src/widgets/card/card_content.dart index 6ca8285bd..5a36f9c72 100644 --- a/forui/lib/src/widgets/card/card_content.dart +++ b/forui/lib/src/widgets/card/card_content.dart @@ -17,7 +17,6 @@ part of 'card.dart'; child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // TODO: Check if TextStyle and font have the same font before scaling. if (title != null) Text(title!, style: style.title.withFont(font)), if (subtitle != null) Text(subtitle!, style: style.subtitle.withFont(font)), if (child != null) @@ -29,18 +28,20 @@ part of 'card.dart'; ), ); } + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties ..add(StringProperty('title', title)) ..add(StringProperty('subtitle', subtitle)) - ..add(DiagnosticsProperty('style', style)); + ..add(DiagnosticsProperty('style', style)); } } /// A card content's style. final class FCardContentStyle with Diagnosticable { + /// The padding. final EdgeInsets padding; @@ -77,9 +78,9 @@ final class FCardContentStyle with Diagnosticable { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('padding', padding)) - ..add(DiagnosticsProperty('title', title)) - ..add(DiagnosticsProperty('subtitle', subtitle)); + ..add(DiagnosticsProperty('padding', padding)) + ..add(DiagnosticsProperty('title', title)) + ..add(DiagnosticsProperty('subtitle', subtitle)); } @override @@ -91,4 +92,5 @@ final class FCardContentStyle with Diagnosticable { @override int get hashCode => padding.hashCode ^ title.hashCode ^ subtitle.hashCode; + } diff --git a/forui/lib/src/widgets/separator.dart b/forui/lib/src/widgets/separator.dart index 1239b04e2..6e4daae45 100644 --- a/forui/lib/src/widgets/separator.dart +++ b/forui/lib/src/widgets/separator.dart @@ -3,13 +3,13 @@ import 'package:flutter/widgets.dart'; import 'package:forui/forui.dart'; -/// A separator visually separates content. +/// A separator that visually separates content. final class FSeparator extends StatelessWidget { - /// The style. + /// The style. Defaults to the appropriate style in [FThemeData.separatorStyles] if null. final FSeparatorStyle? style; - /// Whether this separator should be horizontal or vertical. Defaults to horizontal (false). + /// True if this separator is vertical. Defaults to false (horizontal). final bool vertical; /// Creates a [FSeparator]. @@ -17,32 +17,33 @@ final class FSeparator extends StatelessWidget { @override Widget build(BuildContext context) { - final style = this.style ?? (vertical ? context.theme.separatorStyles.vertical : context.theme.separatorStyles.horizontal); + final FSeparatorStyle(:padding, :width, :color) = style ?? (vertical ? context.theme.separatorStyles.vertical : context.theme.separatorStyles.horizontal); return Padding( - padding: style.padding, + padding: padding, child: vertical ? SizedBox( - width: style.width, + width: width, height: double.infinity, child: ColoredBox( - color: style.color, + color: color, ), ) : SizedBox( width: double.infinity, - height: style.width, + height: width, child: ColoredBox( - color: style.color, + color: color, ), ), ); } + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('vertical', vertical)) - ..add(DiagnosticsProperty('style', style)); + ..add(FlagProperty('vertical', value: vertical, defaultValue: false)) + ..add(DiagnosticsProperty('style', style)); } } @@ -82,8 +83,8 @@ final class FSeparatorStyles with Diagnosticable { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('horizontal', horizontal)) - ..add(DiagnosticsProperty('vertical', vertical)); + ..add(DiagnosticsProperty('horizontal', horizontal)) + ..add(DiagnosticsProperty('vertical', vertical)); } @override @@ -111,7 +112,7 @@ final class FSeparatorStyle with Diagnosticable { /// The color of the separating line. Defaults to [FColorScheme.secondary]. final Color color; - /// The padding surrounding the separating line. + /// The padding surrounding the separating line. Defaults to the appropriate padding in [defaultPadding]. final EdgeInsetsGeometry padding; /// The width of the separating line. Defaults to 1. @@ -141,7 +142,7 @@ final class FSeparatorStyle with Diagnosticable { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('padding', padding)) + ..add(DiagnosticsProperty('padding', padding)) ..add(ColorProperty('color', color)) ..add(DoubleProperty('width', width)); } diff --git a/forui/lib/src/widgets/switch.dart b/forui/lib/src/widgets/switch.dart index 44fc26e8b..12740396a 100644 --- a/forui/lib/src/widgets/switch.dart +++ b/forui/lib/src/widgets/switch.dart @@ -4,16 +4,16 @@ import 'package:flutter/gestures.dart'; import 'package:forui/forui.dart'; -/// A control that allows the user to toggle between checked and not checked. +/// A control that allows the user to toggle between checked and unchecked. class FSwitch extends StatelessWidget { - /// The style of the switch. + /// The style. Defaults to the appropriate style in [FThemeData.switchStyle] if null. final FSwitchStyle? style; - /// Whether this switch is on or off. + /// True if this switch is checked, and false if unchecked. final bool value; - /// Called when the user toggles with switch on or off. + /// Called when the user toggles the switch on or off. /// /// The switch passes the new value to the callback but does not actually /// change state until the parent widget rebuilds the switch with the new @@ -108,29 +108,30 @@ class FSwitch extends StatelessWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('value', value)) - ..add(DiagnosticsProperty('autofocus', autofocus)) - ..add(DiagnosticsProperty('dragStartBehavior', dragStartBehavior)) ..add(DiagnosticsProperty('style', style)) - ..add(DiagnosticsProperty>('onChanged', onChanged)) - ..add(DiagnosticsProperty('focusNode', focusNode)) - ..add(DiagnosticsProperty>('onFocusChange', onFocusChange)); + ..add(FlagProperty('value', value: value)) + ..add(FlagProperty('autofocus', value: autofocus, defaultValue: false)) + ..add(EnumProperty('dragStartBehavior', dragStartBehavior, defaultValue: DragStartBehavior.start)) + ..add(DiagnosticsProperty('onChanged', onChanged, defaultValue: null)) + ..add(DiagnosticsProperty('focusNode', focusNode, defaultValue: null)) + ..add(DiagnosticsProperty('onFocusChange', onFocusChange, defaultValue: null)); } + } /// The style of a [FSwitch]. final class FSwitchStyle with Diagnosticable { - /// The color of the switch when it is checked. + /// The track's color when checked. Defaults to [FColorScheme.primary]. final Color checked; - /// The color of the switch when it is unchecked. + /// The track's color when unchecked. Defaults to [FColorScheme.border]. final Color unchecked; - /// The color of the switch's thumb. + /// The thumb's color. Defaults to [FColorScheme.background]. final Color thumb; - /// The color of the switch when it is focused. Defaults to a slightly transparent [checked] color. + /// This switch's color when focused. Defaults to a slightly transparent [checked] color. final Color focus; /// Creates a [FSwitchStyle]. diff --git a/forui/test/golden/button/zinc-dark-FButtonVariant.destructive-button-content.png b/forui/test/golden/button/zinc-dark-FButtonVariant.destructive-enabled-button-content.png similarity index 100% rename from forui/test/golden/button/zinc-dark-FButtonVariant.destructive-button-content.png rename to forui/test/golden/button/zinc-dark-FButtonVariant.destructive-enabled-button-content.png diff --git a/forui/test/golden/button/zinc-dark-FButtonVariant.destructive-raw-content.png b/forui/test/golden/button/zinc-dark-FButtonVariant.destructive-enabled-raw-content.png similarity index 100% rename from forui/test/golden/button/zinc-dark-FButtonVariant.destructive-raw-content.png rename to forui/test/golden/button/zinc-dark-FButtonVariant.destructive-enabled-raw-content.png diff --git a/forui/test/golden/button/zinc-dark-FButtonVariant.outlined-button-content.png b/forui/test/golden/button/zinc-dark-FButtonVariant.outlined-enabled-button-content.png similarity index 100% rename from forui/test/golden/button/zinc-dark-FButtonVariant.outlined-button-content.png rename to forui/test/golden/button/zinc-dark-FButtonVariant.outlined-enabled-button-content.png diff --git a/forui/test/golden/button/zinc-dark-FButtonVariant.outlined-raw-content.png b/forui/test/golden/button/zinc-dark-FButtonVariant.outlined-enabled-raw-content.png similarity index 100% rename from forui/test/golden/button/zinc-dark-FButtonVariant.outlined-raw-content.png rename to forui/test/golden/button/zinc-dark-FButtonVariant.outlined-enabled-raw-content.png diff --git a/forui/test/golden/button/zinc-dark-FButtonVariant.primary-button-content.png b/forui/test/golden/button/zinc-dark-FButtonVariant.primary-enabled-button-content.png similarity index 100% rename from forui/test/golden/button/zinc-dark-FButtonVariant.primary-button-content.png rename to forui/test/golden/button/zinc-dark-FButtonVariant.primary-enabled-button-content.png diff --git a/forui/test/golden/button/zinc-dark-FButtonVariant.primary-raw-content.png b/forui/test/golden/button/zinc-dark-FButtonVariant.primary-enabled-raw-content.png similarity index 100% rename from forui/test/golden/button/zinc-dark-FButtonVariant.primary-raw-content.png rename to forui/test/golden/button/zinc-dark-FButtonVariant.primary-enabled-raw-content.png diff --git a/forui/test/golden/button/zinc-dark-FButtonVariant.secondary-button-content.png b/forui/test/golden/button/zinc-dark-FButtonVariant.secondary-enabled-button-content.png similarity index 100% rename from forui/test/golden/button/zinc-dark-FButtonVariant.secondary-button-content.png rename to forui/test/golden/button/zinc-dark-FButtonVariant.secondary-enabled-button-content.png diff --git a/forui/test/golden/button/zinc-dark-FButtonVariant.secondary-raw-content.png b/forui/test/golden/button/zinc-dark-FButtonVariant.secondary-enabled-raw-content.png similarity index 100% rename from forui/test/golden/button/zinc-dark-FButtonVariant.secondary-raw-content.png rename to forui/test/golden/button/zinc-dark-FButtonVariant.secondary-enabled-raw-content.png diff --git a/forui/test/golden/button/zinc-light-FButtonVariant.destructive-button-content.png b/forui/test/golden/button/zinc-light-FButtonVariant.destructive-enabled-button-content.png similarity index 100% rename from forui/test/golden/button/zinc-light-FButtonVariant.destructive-button-content.png rename to forui/test/golden/button/zinc-light-FButtonVariant.destructive-enabled-button-content.png diff --git a/forui/test/golden/button/zinc-light-FButtonVariant.destructive-raw-content.png b/forui/test/golden/button/zinc-light-FButtonVariant.destructive-enabled-raw-content.png similarity index 100% rename from forui/test/golden/button/zinc-light-FButtonVariant.destructive-raw-content.png rename to forui/test/golden/button/zinc-light-FButtonVariant.destructive-enabled-raw-content.png diff --git a/forui/test/golden/button/zinc-light-FButtonVariant.outlined-button-content.png b/forui/test/golden/button/zinc-light-FButtonVariant.outlined-enabled-button-content.png similarity index 100% rename from forui/test/golden/button/zinc-light-FButtonVariant.outlined-button-content.png rename to forui/test/golden/button/zinc-light-FButtonVariant.outlined-enabled-button-content.png diff --git a/forui/test/golden/button/zinc-light-FButtonVariant.outlined-raw-content.png b/forui/test/golden/button/zinc-light-FButtonVariant.outlined-enabled-raw-content.png similarity index 100% rename from forui/test/golden/button/zinc-light-FButtonVariant.outlined-raw-content.png rename to forui/test/golden/button/zinc-light-FButtonVariant.outlined-enabled-raw-content.png diff --git a/forui/test/golden/button/zinc-light-FButtonVariant.primary-button-content.png b/forui/test/golden/button/zinc-light-FButtonVariant.primary-enabled-button-content.png similarity index 100% rename from forui/test/golden/button/zinc-light-FButtonVariant.primary-button-content.png rename to forui/test/golden/button/zinc-light-FButtonVariant.primary-enabled-button-content.png diff --git a/forui/test/golden/button/zinc-light-FButtonVariant.primary-raw-content.png b/forui/test/golden/button/zinc-light-FButtonVariant.primary-enabled-raw-content.png similarity index 100% rename from forui/test/golden/button/zinc-light-FButtonVariant.primary-raw-content.png rename to forui/test/golden/button/zinc-light-FButtonVariant.primary-enabled-raw-content.png diff --git a/forui/test/golden/button/zinc-light-FButtonVariant.secondary-button-content.png b/forui/test/golden/button/zinc-light-FButtonVariant.secondary-enabled-button-content.png similarity index 100% rename from forui/test/golden/button/zinc-light-FButtonVariant.secondary-button-content.png rename to forui/test/golden/button/zinc-light-FButtonVariant.secondary-enabled-button-content.png diff --git a/forui/test/golden/button/zinc-light-FButtonVariant.secondary-raw-content.png b/forui/test/golden/button/zinc-light-FButtonVariant.secondary-enabled-raw-content.png similarity index 100% rename from forui/test/golden/button/zinc-light-FButtonVariant.secondary-raw-content.png rename to forui/test/golden/button/zinc-light-FButtonVariant.secondary-enabled-raw-content.png diff --git a/forui/test/src/widgets/button_test.dart b/forui/test/src/widgets/button_test.dart index 2a5f1cb0b..c6c3d9e27 100644 --- a/forui/test/src/widgets/button_test.dart +++ b/forui/test/src/widgets/button_test.dart @@ -12,7 +12,7 @@ void main() { group('FButton', () { for (final (name, theme, _) in TestScaffold.themes) { for (final variant in FButtonVariant.values) { - testWidgets('$name with FButtonContent', (tester) async { + testWidgets('$name enabled with FButtonContent', (tester) async { await tester.pumpWidget( TestScaffold( data: theme, @@ -21,7 +21,7 @@ void main() { child: FButton( text: 'Button', design: variant, - onPressed: () {}, + onPress: () {}, ), ), ), @@ -29,7 +29,7 @@ void main() { await expectLater( find.byType(TestScaffold), - matchesGoldenFile('button/$name-$variant-button-content.png'), + matchesGoldenFile('button/$name-$variant-enabled-button-content.png'), ); }); @@ -42,6 +42,7 @@ void main() { child: FButton( text: 'Button', design: variant, + onPress: null, ), ), ), @@ -53,7 +54,7 @@ void main() { ); }); - testWidgets('$name with raw content', (tester) async { + testWidgets('$name with enabled raw content', (tester) async { await tester.pumpWidget( TestScaffold( data: theme, @@ -61,7 +62,7 @@ void main() { padding: const EdgeInsets.symmetric(horizontal: 20), child: FButton.raw( design: variant, - onPressed: () {}, + onPress: () {}, builder: (_, style) => Padding( padding: const EdgeInsets.all(50), child: Container( @@ -88,7 +89,7 @@ void main() { await expectLater( find.byType(TestScaffold), - matchesGoldenFile('button/$name-$variant-raw-content.png'), + matchesGoldenFile('button/$name-$variant-enabled-raw-content.png'), ); }); @@ -100,7 +101,7 @@ void main() { padding: const EdgeInsets.symmetric(horizontal: 20), child: FButton.raw( design: variant, - onPressed: null, + onPress: null, builder: (_, style) => Padding( padding: const EdgeInsets.all(50), child: Container(