From 198c3b82eb0f5b8ebcc6d2c9085752241e21b42c Mon Sep 17 00:00:00 2001 From: Matthias Ngeo Date: Fri, 24 May 2024 10:47:05 +0800 Subject: [PATCH] Add separator (#21) * Fix PR issues * WIP separator * Adjust separator default values * Tweak separator even more * Tidy up separator implementation * Commit from GitHub Actions (Forui Presubmit) * Fix PR issues * Add global border width --------- Co-authored-by: Pante --- forui/example/pubspec.yaml | 4 +- forui/lib/forui.dart | 1 + forui/lib/src/theme/font.dart | 10 +-- forui/lib/src/theme/style.dart | 16 ++-- forui/lib/src/theme/theme_data.dart | 48 ++++++++--- forui/lib/src/widgets/card/card_style.dart | 7 +- .../lib/src/widgets/separator/separator.dart | 84 +++++++++++++++++++ 7 files changed, 147 insertions(+), 23 deletions(-) create mode 100644 forui/lib/src/widgets/separator/separator.dart diff --git a/forui/example/pubspec.yaml b/forui/example/pubspec.yaml index 683f79b96..4b6c4a6c8 100644 --- a/forui/example/pubspec.yaml +++ b/forui/example/pubspec.yaml @@ -1,5 +1,5 @@ -name: samples -description: "Forui Samples" +name: forui_example +description: "Forui Example" # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev diff --git a/forui/lib/forui.dart b/forui/lib/forui.dart index 77e82779c..d3be64c3f 100644 --- a/forui/lib/forui.dart +++ b/forui/lib/forui.dart @@ -15,3 +15,4 @@ export 'src/theme/themes.dart'; // Widgets export 'src/widgets/box/box.dart'; export 'src/widgets/card/card.dart'; +export 'src/widgets/separator/separator.dart'; diff --git a/forui/lib/src/theme/font.dart b/forui/lib/src/theme/font.dart index f3f57ff94..839cef49f 100644 --- a/forui/lib/src/theme/font.dart +++ b/forui/lib/src/theme/font.dart @@ -59,10 +59,10 @@ final class FFont with Diagnosticable { this.heightScalar = 1, }): assert(family.isNotBlank, 'Font family should not be blank.'), - assert(0 < sizeScalar, 'The sizeScalar is $sizeScalar, but it should be in range "0 < sizeScalar"'), - assert(0 < letterSpacingScalar, 'The letterSpacingScalar is $letterSpacingScalar, but it should be in range "0 < letterSpacingScalar"'), - assert(0 < wordSpacingScalar, 'The wordSpacingScalar is $wordSpacingScalar, but it should be in range "0 < wordSpacingScalar"'), - assert(0 < heightScalar, 'The heightScalar is $heightScalar, but it should be in range "0 < heightScalar"'); + assert(0 < sizeScalar, 'The sizeScalar is $sizeScalar, but it should be in the range "0 < sizeScalar".'), + assert(0 < letterSpacingScalar, 'The letterSpacingScalar is $letterSpacingScalar, but it should be in the range "0 < letterSpacingScalar".'), + assert(0 < wordSpacingScalar, 'The wordSpacingScalar is $wordSpacingScalar, but it should be in the range "0 < wordSpacingScalar".'), + assert(0 < heightScalar, 'The heightScalar is $heightScalar, but it should be in the range "0 < heightScalar".'); /// Creates a copy of this [FFont] with the given properties replaced. FFont copyWith({ @@ -214,7 +214,7 @@ extension FontTextStyle on TextStyle { /// print(style.wordSpacing); // 4 /// print(style.height); // 5 /// ``` - TextStyle withFont(FFont font) => TextStyle( + TextStyle withFont(FFont font) => copyWith( fontFamily: font.family, fontSize: _scale(fontSize, font.sizeScalar), letterSpacing: _scale(letterSpacing, font.letterSpacingScalar), diff --git a/forui/lib/src/theme/style.dart b/forui/lib/src/theme/style.dart index 5d4cdde2a..e601c92e6 100644 --- a/forui/lib/src/theme/style.dart +++ b/forui/lib/src/theme/style.dart @@ -7,15 +7,20 @@ final class FStyle with Diagnosticable { /// The border radius. final BorderRadius borderRadius; + /// The border width. + final double borderWidth; + /// Creates an [FStyle]. - FStyle({BorderRadius? borderRadius}): - borderRadius = borderRadius ?? BorderRadius.circular(8); + FStyle({BorderRadius? borderRadius, double? borderWidth}): + borderRadius = borderRadius ?? BorderRadius.circular(8), + borderWidth = borderWidth ?? 1; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - .add(DiagnosticsProperty('borderRadius', borderRadius)); + ..add(DiagnosticsProperty('borderRadius', borderRadius, defaultValue: BorderRadius.circular(8))) + ..add(DoubleProperty('borderWidth', borderWidth, defaultValue: 1)); } @override @@ -23,9 +28,10 @@ final class FStyle with Diagnosticable { identical(this, other) || other is FStyle && runtimeType == other.runtimeType && - borderRadius == other.borderRadius; + borderRadius == other.borderRadius && + borderWidth == other.borderWidth; @override - int get hashCode => borderRadius.hashCode; + int get hashCode => borderRadius.hashCode ^ borderWidth.hashCode; } diff --git a/forui/lib/src/theme/theme_data.dart b/forui/lib/src/theme/theme_data.dart index 441faf1d1..0596b86cd 100644 --- a/forui/lib/src/theme/theme_data.dart +++ b/forui/lib/src/theme/theme_data.dart @@ -19,6 +19,9 @@ class FThemeData with Diagnosticable { /// The card style. final FCardStyle cardStyle; + /// The separator styles. + final ({FSeparatorStyle horizontal, FSeparatorStyle vertical}) separatorStyles; + /// Creates a [FThemeData]. FThemeData({ required this.colorScheme, @@ -26,6 +29,7 @@ class FThemeData with Diagnosticable { required this.style, required this.boxStyle, required this.cardStyle, + required this.separatorStyles, }); /// Creates a [FThemeData] that inherits the given arguments' properties. @@ -35,7 +39,19 @@ class FThemeData with Diagnosticable { required this.style, }): boxStyle = FBoxStyle.inherit(colorScheme: colorScheme), - cardStyle = FCardStyle.inherit(colorScheme: colorScheme, style: style); + cardStyle = FCardStyle.inherit(colorScheme: colorScheme, style: style), + separatorStyles = ( + horizontal: FSeparatorStyle.inherit( + colorScheme: colorScheme, + style: style, + padding: FSeparatorStyle.defaultPadding.horizontal, + ), + vertical: FSeparatorStyle.inherit( + colorScheme: colorScheme, + style: style, + padding: FSeparatorStyle.defaultPadding.vertical, + ) + ); /// Creates a copy of this [FThemeData] with the given properties replaced. FThemeData copyWith({ @@ -44,12 +60,14 @@ class FThemeData with Diagnosticable { FStyle? style, FBoxStyle? boxStyle, FCardStyle? cardStyle, + ({FSeparatorStyle horizontal, FSeparatorStyle vertical})? separatorStyles, }) => FThemeData( colorScheme: colorScheme ?? this.colorScheme, font: font ?? this.font, style: style ?? this.style, boxStyle: boxStyle ?? this.boxStyle, cardStyle: cardStyle ?? this.cardStyle, + separatorStyles: separatorStyles ?? this.separatorStyles, ); @override @@ -60,18 +78,28 @@ class FThemeData with Diagnosticable { ..add(DiagnosticsProperty('font', font, level: DiagnosticLevel.debug)) ..add(DiagnosticsProperty('style', style, level: DiagnosticLevel.debug)) ..add(DiagnosticsProperty('boxStyle', boxStyle, level: DiagnosticLevel.debug)) - ..add(DiagnosticsProperty('cardStyle', cardStyle, level: DiagnosticLevel.debug)); + ..add(DiagnosticsProperty('cardStyle', cardStyle, level: DiagnosticLevel.debug)) + ..add(DiagnosticsProperty<({FSeparatorStyle horizontal, FSeparatorStyle vertical})>('separatorStyles', separatorStyles, level: DiagnosticLevel.debug)); } @override - bool operator ==(Object other) => identical(this, other) || other is FThemeData && - runtimeType == other.runtimeType && - colorScheme == other.colorScheme && - font == other.font && - style == other.style && - boxStyle == other.boxStyle && - cardStyle == other.cardStyle; + bool operator ==(Object other) => + identical(this, other) || + other is FThemeData && + runtimeType == other.runtimeType && + colorScheme == other.colorScheme && + font == other.font && + style == other.style && + boxStyle == other.boxStyle && + cardStyle == other.cardStyle && + separatorStyles == other.separatorStyles; @override - int get hashCode => colorScheme.hashCode ^ font.hashCode ^ style.hashCode ^ boxStyle.hashCode ^ cardStyle.hashCode; + int get hashCode => + colorScheme.hashCode ^ + font.hashCode ^ + style.hashCode ^ + boxStyle.hashCode ^ + cardStyle.hashCode ^ + separatorStyles.hashCode; } diff --git a/forui/lib/src/widgets/card/card_style.dart b/forui/lib/src/widgets/card/card_style.dart index 56040a7df..d49d91dd2 100644 --- a/forui/lib/src/widgets/card/card_style.dart +++ b/forui/lib/src/widgets/card/card_style.dart @@ -2,6 +2,7 @@ part of 'card.dart'; /// [FCard]'s style. final class FCardStyle with Diagnosticable { + /// The decoration. final BoxDecoration decoration; @@ -14,8 +15,12 @@ final class FCardStyle with Diagnosticable { /// Creates a [FCardStyle] that inherits its properties from [colorScheme] and [style]. FCardStyle.inherit({required FColorScheme colorScheme, required FStyle style}): decoration = BoxDecoration( - border: Border.all(color: colorScheme.border), + border: Border.all( + color: colorScheme.border, + width: style.borderWidth, + ), borderRadius: style.borderRadius, + color: colorScheme.background, ), content = FCardContentStyle.inherit(colorScheme: colorScheme); diff --git a/forui/lib/src/widgets/separator/separator.dart b/forui/lib/src/widgets/separator/separator.dart new file mode 100644 index 000000000..1699c027f --- /dev/null +++ b/forui/lib/src/widgets/separator/separator.dart @@ -0,0 +1,84 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +import 'package:forui/forui.dart'; + +/// A separator visually separates content. +final class FSeparator extends StatelessWidget { + + /// The style. + final FSeparatorStyle? style; + + /// Whether this separator should be horizontal or vertical. Defaults to horizontal (false). + final bool vertical; + + /// Creates a [FSeparator]. + const FSeparator({this.style, this.vertical = false, super.key}); + + @override + Widget build(BuildContext context) { + final style = this.style ?? (vertical ? context.theme.separatorStyles.vertical : context.theme.separatorStyles.horizontal); + return Padding( + padding: style.padding, + child: vertical ? + SizedBox( + width: style.width, + child: ColoredBox( + color: style.color, + ), + ) : + SizedBox( + height: style.width, + child: ColoredBox( + color: style.color, + ), + ), + ); + } + +} + +/// [FSeparator]'s style. +final class FSeparatorStyle with Diagnosticable { + + /// The default padding for horizontal and vertical separators. + static const defaultPadding = ( + horizontal: EdgeInsets.symmetric(vertical: 20), + vertical: EdgeInsets.symmetric(horizontal: 20), + ); + + /// The color of the separating line. Defaults to [FColorScheme.secondary]. + final Color color; + + /// The padding surrounding the separating line. + final EdgeInsetsGeometry padding; + + /// The width of the separating line. Defaults to 1. + /// + /// ## Contract: + /// Throws an [AssertionError] if: + /// * `width` <= 0.0 + /// * `width` is Nan + final double width; + + /// Creates a [FSeparatorStyle]. + FSeparatorStyle({required this.color, required this.padding, this.width = 1}): + assert(0 < width, 'The width is $width, but it should be in the range "0 < width".'); + + /// Creates a [FCardContentStyle] that inherits its properties from [colorScheme]. + FSeparatorStyle.inherit({required FColorScheme colorScheme, required FStyle style, required EdgeInsetsGeometry padding}): + this(color: colorScheme.secondary, padding: padding, width: style.borderWidth); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is FSeparatorStyle && + runtimeType == other.runtimeType && + color == other.color && + padding == other.padding && + width == other.width; + + @override + int get hashCode => color.hashCode ^ padding.hashCode ^ width.hashCode; + +}