diff --git a/forui/example/analysis_options.yaml b/forui/example/analysis_options.yaml index 22b7bee27..831b4a6d4 100644 --- a/forui/example/analysis_options.yaml +++ b/forui/example/analysis_options.yaml @@ -1,4 +1,6 @@ include: package:flint/analysis_options.flutter.yaml analyzer: errors: + diagnostic_describe_all_properties: ignore + public_member_api_docs: ignore unused_result: ignore diff --git a/forui/example/lib/main.dart b/forui/example/lib/main.dart index 53181a404..29cedeb01 100644 --- a/forui/example/lib/main.dart +++ b/forui/example/lib/main.dart @@ -17,20 +17,66 @@ class Application extends StatelessWidget { data: FThemes.zinc.light, child: Scaffold( backgroundColor: FThemes.zinc.light.colorScheme.background, - body: const Padding( - padding: EdgeInsets.all(16), - child: ExampleWidget(), + body: const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ExampleWidget(), + ], ), ), ), ); } -/// The example widget. -class ExampleWidget extends StatelessWidget { - /// Creates an example widget. +class ExampleWidget extends StatefulWidget { const ExampleWidget({super.key}); @override - Widget build(BuildContext context) => ListView(); + State createState() => _ExampleWidgetState(); +} + +class _ExampleWidgetState extends State { + late WidgetStatesController controller; + late TextEditingController textController; + + @override + void initState() { + super.initState(); + controller = WidgetStatesController(); + textController = TextEditingController()..addListener(() {}); + } + + @override + Widget build(BuildContext context) => Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + // FCard(title: 'Email'), + FTextField( + // enabled: false, + labelText: 'Email', + hintText: 'hannah@foruslabs.com', + helpText: 'This is your public display name.', + // errorText: 'Error', + // statesController: controller, + controller: textController, + ), + // const TextField( + // // enabled: false, + // decoration: InputDecoration( + // labelText: 'Material TextField', + // hintText: 'Email', + // errorText: 'Error text', + // ), + // ), + ], + ), + ); + + @override + void dispose() { + super.dispose(); + controller.dispose(); + textController.dispose(); + } } diff --git a/forui/example/pubspec.lock b/forui/example/pubspec.lock index de2dfafb5..ba91c6e07 100644 --- a/forui/example/pubspec.lock +++ b/forui/example/pubspec.lock @@ -77,18 +77,18 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "1414d6d733a85d8ad2f1dfcb3ea7945759e35a123cb99ccfac75d0758f75edfa" + sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.11" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe url: "https://pub.dev" source: hosted - version: "7.3.0" + version: "7.3.1" built_collection: dependency: transitive description: diff --git a/forui/lib/forui.dart b/forui/lib/forui.dart index 597ec1d13..9ea828b3f 100644 --- a/forui/lib/forui.dart +++ b/forui/lib/forui.dart @@ -11,7 +11,7 @@ export 'src/theme/style.dart'; export 'src/theme/theme.dart'; export 'src/theme/theme_data.dart'; -export 'src/theme/font.dart'; +export 'src/theme/typography.dart'; // Themes export 'src/theme/themes.dart'; diff --git a/forui/lib/src/theme/color_scheme.dart b/forui/lib/src/theme/color_scheme.dart index f8e8e4d0d..3d835b8e5 100644 --- a/forui/lib/src/theme/color_scheme.dart +++ b/forui/lib/src/theme/color_scheme.dart @@ -41,6 +41,12 @@ final class FColorScheme with Diagnosticable { /// The destructive foreground color. final Color destructiveForeground; + /// The error color. + final Color error; + + /// The error foreground color. + final Color errorForeground; + /// The border color. final Color border; @@ -57,6 +63,8 @@ final class FColorScheme with Diagnosticable { required this.mutedForeground, required this.destructive, required this.destructiveForeground, + required this.error, + required this.errorForeground, required this.border, }); @@ -73,6 +81,8 @@ final class FColorScheme with Diagnosticable { Color? mutedForeground, Color? destructive, Color? destructiveForeground, + Color? error, + Color? errorForeground, Color? border, }) => FColorScheme( @@ -87,6 +97,8 @@ final class FColorScheme with Diagnosticable { mutedForeground: mutedForeground ?? this.mutedForeground, destructive: destructive ?? this.destructive, destructiveForeground: destructiveForeground ?? this.destructiveForeground, + error: error ?? this.error, + errorForeground: errorForeground ?? this.errorForeground, border: border ?? this.border, ); @@ -105,6 +117,8 @@ final class FColorScheme with Diagnosticable { ..add(ColorProperty('mutedForeground', mutedForeground)) ..add(ColorProperty('destructive', destructive)) ..add(ColorProperty('destructiveForeground', destructiveForeground)) + ..add(ColorProperty('error', error)) + ..add(ColorProperty('errorForeground', errorForeground)) ..add(ColorProperty('border', border)); } @@ -121,6 +135,8 @@ final class FColorScheme with Diagnosticable { mutedForeground == other.mutedForeground && destructive == other.destructive && destructiveForeground == other.destructiveForeground && + error == other.error && + errorForeground == other.errorForeground && border == other.border; @override @@ -136,6 +152,8 @@ final class FColorScheme with Diagnosticable { mutedForeground.hashCode ^ destructive.hashCode ^ destructiveForeground.hashCode ^ + error.hashCode ^ + errorForeground.hashCode ^ border.hashCode; } diff --git a/forui/lib/src/theme/theme.dart b/forui/lib/src/theme/theme.dart index c3a4b2a17..a93b7f15c 100644 --- a/forui/lib/src/theme/theme.dart +++ b/forui/lib/src/theme/theme.dart @@ -41,8 +41,8 @@ class FTheme extends StatelessWidget { child: Directionality( textDirection: textDirection ?? Directionality.of(context), child: DefaultTextStyle( - style: data.font.toTextStyle( - fontSize: data.font.base, + style: data.typography.toTextStyle( + fontSize: data.typography.base, color: data.colorScheme.foreground, ), child: child, diff --git a/forui/lib/src/theme/theme_data.dart b/forui/lib/src/theme/theme_data.dart index d6970382f..fed768c90 100644 --- a/forui/lib/src/theme/theme_data.dart +++ b/forui/lib/src/theme/theme_data.dart @@ -7,8 +7,8 @@ class FThemeData with Diagnosticable { /// The color scheme. final FColorScheme colorScheme; - /// The font data. - final FFont font; + /// The typography data. + final FTypography typography; /// The overarching style. final FStyle style; @@ -40,7 +40,7 @@ class FThemeData with Diagnosticable { /// Creates a [FThemeData]. FThemeData({ required this.colorScheme, - required this.font, + required this.typography, required this.style, required this.badgeStyles, required this.buttonStyles, @@ -55,17 +55,17 @@ class FThemeData with Diagnosticable { /// Creates a [FThemeData] that inherits the given properties. FThemeData.inherit({ required this.colorScheme, - required this.font, + required this.typography, required this.style, - }) : badgeStyles = FBadgeStyles.inherit(colorScheme: colorScheme, font: font, style: style), + }) : badgeStyles = FBadgeStyles.inherit(colorScheme: colorScheme, font: typography, style: style), buttonStyles = FButtonStyles.inherit( colorScheme: colorScheme, - font: font, + font: typography, style: style, ), - cardStyle = FCardStyle.inherit(colorScheme: colorScheme, font: font, style: style), - headerStyle = FHeaderStyle.inherit(colorScheme: colorScheme, font: font), - textFieldStyle = FTextFieldStyle.inherit(colorScheme: colorScheme, font: font, style: style), + cardStyle = FCardStyle.inherit(colorScheme: colorScheme, font: typography, style: style), + headerStyle = FHeaderStyle.inherit(colorScheme: colorScheme, font: typography), + textFieldStyle = FTextFieldStyle.inherit(colorScheme: colorScheme, font: typography, style: style), boxStyle = FBoxStyle.inherit(colorScheme: colorScheme), separatorStyles = FSeparatorStyles.inherit(colorScheme: colorScheme, style: style), switchStyle = FSwitchStyle.inherit(colorScheme: colorScheme); @@ -73,7 +73,7 @@ class FThemeData with Diagnosticable { /// Creates a copy of this [FThemeData] with the given properties replaced. FThemeData copyWith({ FColorScheme? colorScheme, - FFont? font, + FTypography? typography, FStyle? style, FBadgeStyles? badgeStyles, FButtonStyles? buttonStyles, @@ -86,7 +86,7 @@ class FThemeData with Diagnosticable { }) => FThemeData( colorScheme: colorScheme ?? this.colorScheme, - font: font ?? this.font, + typography: typography ?? this.typography, style: style ?? this.style, badgeStyles: badgeStyles ?? this.badgeStyles, buttonStyles: buttonStyles ?? this.buttonStyles, @@ -103,7 +103,7 @@ class FThemeData with Diagnosticable { super.debugFillProperties(properties); properties ..add(DiagnosticsProperty('colorScheme', colorScheme, level: DiagnosticLevel.debug)) - ..add(DiagnosticsProperty('font', font, level: DiagnosticLevel.debug)) + ..add(DiagnosticsProperty('typography', typography, level: DiagnosticLevel.debug)) ..add(DiagnosticsProperty('style', style, level: DiagnosticLevel.debug)) ..add(DiagnosticsProperty('badgeStyles', badgeStyles, level: DiagnosticLevel.debug)) ..add(DiagnosticsProperty('buttonStyles', buttonStyles, level: DiagnosticLevel.debug)) @@ -121,7 +121,7 @@ class FThemeData with Diagnosticable { other is FThemeData && runtimeType == other.runtimeType && colorScheme == other.colorScheme && - font == other.font && + typography == other.typography && style == other.style && badgeStyles == other.badgeStyles && buttonStyles == other.buttonStyles && @@ -135,7 +135,7 @@ class FThemeData with Diagnosticable { @override int get hashCode => colorScheme.hashCode ^ - font.hashCode ^ + typography.hashCode ^ style.hashCode ^ badgeStyles.hashCode ^ buttonStyles.hashCode ^ diff --git a/forui/lib/src/theme/themes.dart b/forui/lib/src/theme/themes.dart index 7f02d79cc..db48af43e 100644 --- a/forui/lib/src/theme/themes.dart +++ b/forui/lib/src/theme/themes.dart @@ -8,7 +8,7 @@ extension FThemes on Never { /// The light and dark variants of the [Zinc](https://ui.shadcn.com/themes) theme. static final zinc = ( light: FThemeData.inherit( - font: FFont(), + typography: FTypography(), colorScheme: const FColorScheme( brightness: Brightness.light, background: Color(0xFFFFFFFF), @@ -21,12 +21,14 @@ extension FThemes on Never { mutedForeground: Color(0xFF71717A), destructive: Color(0xFFEF4444), destructiveForeground: Color(0xFFFAFAFA), + error: Color(0xFFEF4444), + errorForeground: Color(0xFFFAFAFA), border: Color(0xFFE4E4E7), ), style: FStyle(), ), dark: FThemeData.inherit( - font: FFont(), + typography: FTypography(), colorScheme: const FColorScheme( brightness: Brightness.dark, background: Color(0xFF09090B), @@ -39,6 +41,8 @@ extension FThemes on Never { mutedForeground: Color(0xFFA1A1AA), destructive: Color(0xFF7F1D1D), destructiveForeground: Color(0xFFFAFAFA), + error: Color(0xFF7F1D1D), + errorForeground: Color(0xFFFAFAFA), border: Color(0xFF27272A), ), style: FStyle(), diff --git a/forui/lib/src/theme/font.dart b/forui/lib/src/theme/typography.dart similarity index 87% rename from forui/lib/src/theme/font.dart rename to forui/lib/src/theme/typography.dart index 2474a3c87..13ab553a0 100644 --- a/forui/lib/src/theme/font.dart +++ b/forui/lib/src/theme/typography.dart @@ -8,18 +8,18 @@ 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 used to configure the Forui widgets' [TextStyle]s. +/// A Forui typography used to configure the Forui widgets' [TextStyle]s. /// -/// It is usually inherited from an ancestor [FTheme]. Besides the typical font information, a [FFont] also contains +/// It is usually inherited from an ancestor [FTheme]. Besides the typical font information, a [FTypography] 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 { +final class FTypography with Diagnosticable { - /// The font family. Defaults to [`packages/forui/Inter`](https://fonts.google.com/specimen/Inter). + /// The default font family. Defaults to [`packages/forui/Inter`](https://fonts.google.com/specimen/Inter). /// /// ## Contract: /// Throws an [AssertionError] if blank. - final String family; + final String defaultFontFamily; /// A value used to scale [TextStyle.fontSize]. Defaults to 1. /// @@ -149,9 +149,9 @@ final class FFont with Diagnosticable { /// * `xl8` is NaN final double xl8; - /// Creates a [FFont]. - FFont({ - this.family = 'packages/forui/Inter', + /// Creates a [FTypography]. + FTypography({ + this.defaultFontFamily = 'packages/forui/Inter', this.sizeScalar = 1, this.letterSpacingScalar = 1, this.wordSpacingScalar = 1, @@ -169,7 +169,7 @@ final class FFont with Diagnosticable { this.xl7 = 72, this.xl8 = 96, }): - assert(family.isNotBlank, 'Font family should not be blank.'), + assert(defaultFontFamily.isNotBlank, 'Font family should not be blank.'), 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".'), @@ -187,9 +187,9 @@ final class FFont with Diagnosticable { assert(0 < xl7, 'The xl7 is $xl7, but it should be in the range "0 < xl7".'), assert(0 < xl8, 'The xl8 is $xl8, but it should be in the range "0 < xl8".'); - /// Creates a copy of this [FFont] with the given properties replaced. - FFont copyWith({ - String? family, + /// Creates a copy of this [FTypography] with the given properties replaced. + FTypography copyWith({ + String? defaultFontFamily, double? sizeScalar, double? letterSpacingScalar, double? wordSpacingScalar, @@ -207,8 +207,8 @@ final class FFont with Diagnosticable { double? xl7, double? xl8, }) => - FFont( - family: family ?? this.family, + FTypography( + defaultFontFamily: defaultFontFamily ?? this.defaultFontFamily, sizeScalar: sizeScalar ?? this.sizeScalar, letterSpacingScalar: letterSpacingScalar ?? this.letterSpacingScalar, wordSpacingScalar: wordSpacingScalar ?? this.wordSpacingScalar, @@ -227,7 +227,7 @@ final class FFont with Diagnosticable { xl8: xl8 ?? this.xl8, ); - /// Returns a [TextStyle] with the given properties, based on and scaled using this [FFont]. + /// Returns a [TextStyle] with the given properties, based on and scaled using this [FTypography]. /// /// ```dart /// final font = FFont( @@ -298,7 +298,7 @@ final class FFont with Diagnosticable { decorationStyle: decorationStyle, decorationThickness: decorationThickness, debugLabel: debugLabel, - fontFamily: family, + fontFamily: defaultFontFamily, overflow: overflow, ); @@ -306,7 +306,7 @@ final class FFont with Diagnosticable { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(StringProperty('family', family, defaultValue: 'packages/forui/Inter')) + ..add(StringProperty('family', defaultFontFamily, defaultValue: 'packages/forui/Inter')) ..add(DoubleProperty('sizeScalar', sizeScalar, defaultValue: 1)) ..add(DoubleProperty('letterSpacingScalar', letterSpacingScalar, defaultValue: 1)) ..add(DoubleProperty('wordSpacingScalar', wordSpacingScalar, defaultValue: 1)) @@ -328,9 +328,9 @@ final class FFont with Diagnosticable { @override bool operator ==(Object other) => identical(this, other) || - other is FFont && + other is FTypography && runtimeType == other.runtimeType && - family == other.family && + defaultFontFamily == other.defaultFontFamily && sizeScalar == other.sizeScalar && letterSpacingScalar == other.letterSpacingScalar && wordSpacingScalar == other.wordSpacingScalar && @@ -350,7 +350,7 @@ final class FFont with Diagnosticable { @override int get hashCode => - family.hashCode ^ + defaultFontFamily.hashCode ^ sizeScalar.hashCode ^ letterSpacingScalar.hashCode ^ wordSpacingScalar.hashCode ^ @@ -369,14 +369,14 @@ final class FFont with Diagnosticable { xl8.hashCode; } -/// Provides functions for working with [FFont]s. -extension FontTextStyle on TextStyle { +/// Provides functions for working with [FTypography]s. +extension TypographyTextStyle on TextStyle { - /// Returns a [TextStyle] with the given [font], scaled using it. + /// Returns a [TextStyle] scaled using the given [typography]. /// /// ```dart - /// final font = FFont( - /// family: 'packages/forui/my-font', + /// final typography = FTypography( + /// defaultFontFamily: 'packages/forui/my-font', /// sizeScalar: 2, /// letterSpacingScalar: 3, /// wordSpacingScalar: 4, @@ -389,20 +389,19 @@ extension FontTextStyle on TextStyle { /// letterSpacing: 1, /// wordSpacing: 1, /// height: 1, - /// ).withFont(font); + /// ).scale(typography); /// - /// print(style.fontFamily); // 'packages/forui/my-font' + /// print(style.fontFamily); // 'default-font' /// print(style.fontSize); // 2 /// print(style.letterSpacing); // 3 /// print(style.wordSpacing); // 4 /// print(style.height); // 5 /// ``` - TextStyle withFont(FFont font) => copyWith( - fontFamily: font.family, - fontSize: _scale(fontSize, font.sizeScalar), - letterSpacing: _scale(letterSpacing, font.letterSpacingScalar), - wordSpacing: _scale(wordSpacing, font.wordSpacingScalar), - height: _scale(height, font.heightScalar), + TextStyle scale(FTypography typography) => copyWith( + fontSize: _scale(fontSize, typography.sizeScalar), + letterSpacing: _scale(letterSpacing, typography.letterSpacingScalar), + wordSpacing: _scale(wordSpacing, typography.wordSpacingScalar), + height: _scale(height, typography.heightScalar), ); } diff --git a/forui/lib/src/widgets/badge/badge_content.dart b/forui/lib/src/widgets/badge/badge_content.dart index e85d289ba..a4892633c 100644 --- a/forui/lib/src/widgets/badge/badge_content.dart +++ b/forui/lib/src/widgets/badge/badge_content.dart @@ -10,7 +10,7 @@ part of 'badge.dart'; Widget build(BuildContext context) => Center( child: Padding( padding: style.content.padding, - child: Text(label, style: style.content.label.withFont(context.theme.font)), + child: Text(label, style: style.content.label.scale(context.theme.typography)), ), ); diff --git a/forui/lib/src/widgets/badge/badge_styles.dart b/forui/lib/src/widgets/badge/badge_styles.dart index f841ecdd1..9ede642e9 100644 --- a/forui/lib/src/widgets/badge/badge_styles.dart +++ b/forui/lib/src/widgets/badge/badge_styles.dart @@ -18,7 +18,7 @@ class FBadgeStyles with Diagnosticable { FBadgeStyles({required this.primary, required this.secondary, required this.outline, required this.destructive}); /// Creates a [FBadgeStyles] that inherits its properties from [colorScheme] and [style]. - FBadgeStyles.inherit({required FColorScheme colorScheme, required FFont font, required FStyle style}): + FBadgeStyles.inherit({required FColorScheme colorScheme, required FTypography font, required FStyle style}): primary = FBadgeStyle.inherit( style: style, background: colorScheme.primary, diff --git a/forui/lib/src/widgets/box.dart b/forui/lib/src/widgets/box.dart index 27b04bec4..4ecc967ae 100644 --- a/forui/lib/src/widgets/box.dart +++ b/forui/lib/src/widgets/box.dart @@ -21,7 +21,7 @@ final class FBox extends StatelessWidget { color: color, child: Text( this.text, - style: text.withFont(context.theme.font), + style: text.scale(context.theme.typography), ), ); } diff --git a/forui/lib/src/widgets/button/button_content_style.dart b/forui/lib/src/widgets/button/button_content_style.dart index 3c14abcbc..b87cfdd53 100644 --- a/forui/lib/src/widgets/button/button_content_style.dart +++ b/forui/lib/src/widgets/button/button_content_style.dart @@ -27,7 +27,7 @@ class FButtonContentStyle with Diagnosticable { }); /// Creates a [FButtonContentStyle] that inherits its properties from the given [foreground] and [disabledForeground]. - FButtonContentStyle.inherit({ required FFont font,required Color foreground, required Color disabledForeground}) + FButtonContentStyle.inherit({ required FTypography font,required Color foreground, required Color disabledForeground}) : padding = const EdgeInsets.symmetric( horizontal: 5, vertical: 17, diff --git a/forui/lib/src/widgets/button/button_styles.dart b/forui/lib/src/widgets/button/button_styles.dart index d66d044a1..37b4b1fd4 100644 --- a/forui/lib/src/widgets/button/button_styles.dart +++ b/forui/lib/src/widgets/button/button_styles.dart @@ -23,7 +23,7 @@ class FButtonStyles with Diagnosticable{ }); /// Creates a [FButtonStyle] that inherits its properties from [colorScheme]. - FButtonStyles.inherit({required FColorScheme colorScheme, required FFont font, required FStyle style}) + FButtonStyles.inherit({required FColorScheme colorScheme, required FTypography font, required FStyle style}) : primary = FButtonStyle( enabledBoxDecoration: BoxDecoration( borderRadius: style.borderRadius, diff --git a/forui/lib/src/widgets/card/card.dart b/forui/lib/src/widgets/card/card.dart index 9321708b2..0a53722e1 100644 --- a/forui/lib/src/widgets/card/card.dart +++ b/forui/lib/src/widgets/card/card.dart @@ -63,7 +63,7 @@ final class FCardStyle with Diagnosticable { FCardStyle({required this.decoration, required this.content}); /// Creates a [FCardStyle] that inherits its properties from [colorScheme], [font] and [style]. - FCardStyle.inherit({required FColorScheme colorScheme, required FFont font, required FStyle style}): + FCardStyle.inherit({required FColorScheme colorScheme, required FTypography font, required FStyle style}): decoration = BoxDecoration( border: Border.all(color: colorScheme.border), borderRadius: style.borderRadius, diff --git a/forui/lib/src/widgets/card/card_content.dart b/forui/lib/src/widgets/card/card_content.dart index 1dea72b5a..a9723b890 100644 --- a/forui/lib/src/widgets/card/card_content.dart +++ b/forui/lib/src/widgets/card/card_content.dart @@ -10,15 +10,15 @@ part of 'card.dart'; @override Widget build(BuildContext context) { - final font = context.theme.font; + final typography = context.theme.typography; final style = this.style ?? context.theme.cardStyle.content; return Padding( padding: style.padding, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (title != null) Text(title!, style: style.title.withFont(font)), - if (subtitle != null) Text(subtitle!, style: style.subtitle.withFont(font)), + if (title != null) Text(title!, style: style.title.scale(typography)), + if (subtitle != null) Text(subtitle!, style: style.subtitle.scale(typography)), if (child != null) Padding( padding: (title == null && subtitle == null) ? const EdgeInsets.only(top: 4) : const EdgeInsets.only(top: 10), @@ -55,7 +55,7 @@ final class FCardContentStyle with Diagnosticable { const FCardContentStyle({required this.padding, required this.title, required this.subtitle}); /// Creates a [FCardContentStyle] that inherits its properties from [colorScheme] and [font]. - FCardContentStyle.inherit({required FColorScheme colorScheme, required FFont font}): + FCardContentStyle.inherit({required FColorScheme colorScheme, required FTypography font}): padding = const EdgeInsets.fromLTRB(16, 12, 16, 16), title = TextStyle( fontSize: font.base, diff --git a/forui/lib/src/widgets/header/header.dart b/forui/lib/src/widgets/header/header.dart index 63cfeca6f..bc143bb27 100644 --- a/forui/lib/src/widgets/header/header.dart +++ b/forui/lib/src/widgets/header/header.dart @@ -30,7 +30,7 @@ final class FHeader extends StatelessWidget { @override Widget build(BuildContext context) { final style = this.style ?? context.theme.headerStyle; - final font = context.theme.font; + final typography = context.theme.typography; return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -41,7 +41,7 @@ final class FHeader extends StatelessWidget { overflow: TextOverflow.fade, maxLines: 1, softWrap: false, - style: style.title.withFont(font), + style: style.title.scale(typography), ), ), Row(children: actions), @@ -70,8 +70,8 @@ final class FHeaderStyle with Diagnosticable { /// Creates a [FHeaderStyle]. FHeaderStyle({required this.title, required this.action}); - /// Creates a [FHeaderStyle] that inherits its properties from the given [FColorScheme] and [FFont]. - FHeaderStyle.inherit({required FColorScheme colorScheme, required FFont font}) + /// Creates a [FHeaderStyle] that inherits its properties from the given [FColorScheme] and [FTypography]. + FHeaderStyle.inherit({required FColorScheme colorScheme, required FTypography font}) : title = TextStyle( fontSize: font.xl3, fontWeight: FontWeight.w700, diff --git a/forui/lib/src/widgets/text_field/text_field.dart b/forui/lib/src/widgets/text_field/text_field.dart index ae1f9ed79..41aec15c1 100644 --- a/forui/lib/src/widgets/text_field/text_field.dart +++ b/forui/lib/src/widgets/text_field/text_field.dart @@ -8,11 +8,10 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:forui/forui.dart'; -part 'text_field_state.dart'; part 'text_field_style.dart'; /// A text field. -final class FTextField extends StatefulWidget { +final class FTextField extends StatelessWidget { static Widget _defaultContextMenuBuilder( BuildContext context, EditableTextState editableTextState, @@ -21,8 +20,38 @@ final class FTextField extends StatefulWidget { /// The style. final FTextFieldStyle? style; + /// The label text. + final String? labelText; + /// The text to display when the text field is empty. - final String? hint; + /// + /// See [InputDecoration.hintText] for more information. + final String? hintText; + + /// The maximum number of lines the [hintText] can occupy. Defaults to the value of [TextField.maxLines] attribute. + /// + /// See [InputDecoration.hintMaxLines] for more information. + final int? hintMaxLines; + + /// The help text. + /// + /// See [InputDecoration.helperText] for more information. + final String? helpText; + + /// The maximum number of lines the [helpText] can occupy. Defaults to the value of [TextField.maxLines] attribute. + /// + /// See [InputDecoration.helperMaxLines] for more information. + final int? helpMaxLines; + + /// The error text. + /// + /// See [InputDecoration.errorText] for more information. + final String? errorText; + + /// The maximum number of lines the [errorText] can occupy. Defaults to the value of [TextField.maxLines] attribute. + /// + /// See [InputDecoration.errorMaxLines] for more information. + final int? errorMaxLines; /// The configuration for the magnifier of this text field. /// @@ -33,7 +62,7 @@ final class FTextField extends StatefulWidget { /// Controls the text being edited. If null, this widget will create its own [TextEditingController]. final TextEditingController? controller; - /// Defines the keyboard focus for this widget. + /// Defines the keyboard focus for this /// /// See [TextField.focusNode] for more information. final FocusNode? focusNode; @@ -380,7 +409,7 @@ final class FTextField extends StatefulWidget { /// See [TextField.restorationId] for more information. final String? restorationId; - /// Whether iOS 14 Scribble features are enabled for this widget. Defaults to true. + /// Whether iOS 14 Scribble features are enabled for this Defaults to true. /// /// Only available on iPads. final bool scribbleEnabled; @@ -419,9 +448,6 @@ final class FTextField extends StatefulWidget { /// If this configuration is left null, then spell check is disabled by default. final SpellCheckConfiguration? spellCheckConfiguration; - /// The label. - final String? label; - /// The suffix icon. /// /// See [InputDecoration.suffixIcon] for more information. @@ -430,7 +456,13 @@ final class FTextField extends StatefulWidget { /// Creates a [FTextField]. const FTextField({ this.style, - this.hint, + this.labelText, + this.hintText, + this.hintMaxLines, + this.helpText, + this.helpMaxLines, + this.errorText, + this.errorMaxLines, this.magnifierConfiguration, this.controller, this.focusNode, @@ -474,14 +506,19 @@ final class FTextField extends StatefulWidget { this.canRequestFocus = true, this.undoController, this.spellCheckConfiguration, - this.label, this.suffixIcon, }); /// Creates a [FTextField] configured for emails. const FTextField.email({ this.style, - this.hint = 'Email', + this.labelText, + this.hintText = 'Email', + this.hintMaxLines, + this.helpText, + this.helpMaxLines, + this.errorText, + this.errorMaxLines, this.magnifierConfiguration, this.controller, this.focusNode, @@ -525,7 +562,6 @@ final class FTextField extends StatefulWidget { this.canRequestFocus = true, this.undoController, this.spellCheckConfiguration, - this.label, this.suffixIcon, }); @@ -535,7 +571,13 @@ final class FTextField extends StatefulWidget { /// when handling the creation of new passwords. const FTextField.password({ this.style, - this.hint = 'Password', + this.labelText, + this.hintText = 'Password', + this.hintMaxLines, + this.helpText, + this.helpMaxLines, + this.errorText, + this.errorMaxLines, this.magnifierConfiguration, this.controller, this.focusNode, @@ -579,14 +621,19 @@ final class FTextField extends StatefulWidget { this.canRequestFocus = true, this.undoController, this.spellCheckConfiguration, - this.label, this.suffixIcon, }); /// Creates a [FTextField] configured for multiline inputs. const FTextField.multiline({ this.style, - this.hint, + this.labelText, + this.hintText, + this.hintMaxLines, + this.helpText, + this.helpMaxLines, + this.errorText, + this.errorMaxLines, this.magnifierConfiguration, this.controller, this.focusNode, @@ -630,20 +677,184 @@ final class FTextField extends StatefulWidget { this.canRequestFocus = true, this.undoController, this.spellCheckConfiguration, - this.label, this.suffixIcon, }); @override - State createState() => _State(); + Widget build(BuildContext context) { + final theme = context.theme; + final typography = theme.typography; + final style = this.style ?? theme.textFieldStyle; + final stateStyle = switch (this) { + _ when !enabled => style.disabled, + _ when errorText != null => style.error, + _ => style.enabled, + }; + final materialLocalizations = Localizations.of(context, MaterialLocalizations); + + final textField = MergeSemantics( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (labelText != null) + Padding( + padding: const EdgeInsets.only(top: 4, bottom: 7), + child: Text( + labelText!, + style: stateStyle.label, + ), + ), + Material( + color: Colors.transparent, + child: Theme( + // The selection colors are defined in a Theme instead of TextField since TextField does not expose parameters + // for overriding selectionHandleColor. + data: Theme.of(context).copyWith( + textSelectionTheme: TextSelectionThemeData( + cursorColor: style.cursor, + selectionColor: style.cursor.withOpacity(0.4), + selectionHandleColor: style.cursor, + ), + cupertinoOverrideTheme: CupertinoThemeData( + primaryColor: style.cursor, + ), + ), + child: _textField(context, typography, style, stateStyle), + ), + ), + ], + ), + ); + + return materialLocalizations == null ? + Localizations( + locale: Localizations.maybeLocaleOf(context) ?? const Locale('en', 'US'), + delegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + child: textField, + ) : + textField; + } + + Widget _textField( + BuildContext context, + FTypography typography, + FTextFieldStyle style, + FTextFieldStateStyle current, + ) => TextField( + controller: controller, + focusNode: focusNode, + undoController: undoController, + cursorErrorColor: style.cursor, + decoration: InputDecoration( + suffixIcon: suffixIcon, + // See https://stackoverflow.com/questions/70771410/flutter-how-can-i-remove-the-content-padding-for-error-in-textformfield + prefix: Padding(padding: EdgeInsets.only(left: style.contentPadding.left)), + contentPadding: style.contentPadding.copyWith(left: 0), + hintText: hintText, + hintStyle: current.hint.scale(typography), + hintMaxLines: hintMaxLines, + helperText: helpText, + helperStyle: current.footer.scale(typography), + helperMaxLines: helpMaxLines, + errorText: errorText, + errorStyle: current.footer.scale(typography), + errorMaxLines: errorMaxLines, + disabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: style.disabled.unfocused.color, + width: style.disabled.unfocused.width, + ), + borderRadius: style.disabled.unfocused.radius, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: style.enabled.unfocused.color, + width: style.enabled.unfocused.width, + ), + borderRadius: style.enabled.unfocused.radius, + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: style.enabled.focused.color, + width: style.enabled.focused.width, + ), + borderRadius: current.focused.radius, + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: style.error.unfocused.color, + width: style.error.unfocused.width, + ), + borderRadius: style.error.unfocused.radius, + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: style.error.focused.color, + width: style.error.focused.width, + ), + borderRadius: style.error.focused.radius, + ), + ), + keyboardType: keyboardType, + textInputAction: textInputAction, + textCapitalization: textCapitalization, + style: current.content.scale(typography), + textAlign: textAlign, + textAlignVertical: textAlignVertical, + textDirection: textDirection, + readOnly: readOnly, + showCursor: showCursor, + autofocus: autofocus, + statesController: statesController, + obscureText: obscureText, + autocorrect: autocorrect, + smartDashesType: smartDashesType, + smartQuotesType: smartQuotesType, + enableSuggestions: enableSuggestions, + maxLines: maxLines, + minLines: minLines, + expands: expands, + maxLength: maxLength, + maxLengthEnforcement: maxLengthEnforcement, + onChanged: onChange, + onEditingComplete: onEditingComplete, + onSubmitted: onSubmit, + onAppPrivateCommand: onAppPrivateCommand, + inputFormatters: inputFormatters, + enabled: enabled, + ignorePointers: ignorePointers, + keyboardAppearance: style.keyboardAppearance, + scrollPadding: style.scrollPadding, + dragStartBehavior: dragStartBehavior, + selectionControls: selectionControls, + scrollController: scrollController, + scrollPhysics: scrollPhysics, + autofillHints: autofillHints, + restorationId: restorationId, + scribbleEnabled: scribbleEnabled, + enableIMEPersonalizedLearning: enableIMEPersonalizedLearning, + contextMenuBuilder: contextMenuBuilder, + canRequestFocus: canRequestFocus, + spellCheckConfiguration: spellCheckConfiguration, + magnifierConfiguration: magnifierConfiguration, + ); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(StringProperty('label', label)) ..add(DiagnosticsProperty('style', style)) - ..add(StringProperty('hintText', hint)) + ..add(StringProperty('labelText', labelText)) + ..add(StringProperty('hintText', hintText)) + ..add(IntProperty('hintMaxLines', hintMaxLines)) + ..add(StringProperty('helpText', helpText)) + ..add(IntProperty('helpMaxLines', helpMaxLines)) + ..add(StringProperty('errorText', errorText)) + ..add(IntProperty('errorMaxLines', errorMaxLines)) ..add(DiagnosticsProperty('magnifierConfiguration', magnifierConfiguration)) ..add(DiagnosticsProperty('controller', controller)) ..add(DiagnosticsProperty('focusNode', focusNode)) @@ -686,6 +897,7 @@ final class FTextField extends StatefulWidget { ..add(DiagnosticsProperty('contextMenuBuilder', contextMenuBuilder)) ..add(FlagProperty('canRequestFocus', value: canRequestFocus, ifTrue: 'canRequestFocus')) ..add(DiagnosticsProperty('undoController', undoController)) - ..add(DiagnosticsProperty('spellCheckConfiguration', spellCheckConfiguration)); + ..add(DiagnosticsProperty('spellCheckConfiguration', spellCheckConfiguration)) + ..add(DiagnosticsProperty('suffixIcon', suffixIcon)); } } diff --git a/forui/lib/src/widgets/text_field/text_field_state.dart b/forui/lib/src/widgets/text_field/text_field_state.dart deleted file mode 100644 index a430ab8f9..000000000 --- a/forui/lib/src/widgets/text_field/text_field_state.dart +++ /dev/null @@ -1,171 +0,0 @@ -part of 'text_field.dart'; - - -class _State extends State { - - late final WidgetStatesController controller; - - @override - void initState() { - super.initState(); - controller = WidgetStatesController(); - } - - @override - Widget build(BuildContext context) { - final theme = context.theme; - final style = widget.style ?? theme.textFieldStyle; - final materialLocalizations = Localizations.of(context, MaterialLocalizations); - - final textField = MergeSemantics( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (widget.label != null) - Padding( - padding: const EdgeInsets.symmetric(vertical: 5), - child: Text( - widget.label!, - style: style.label, - ), - ), - Material( - color: Colors.transparent, - child: Theme( - // The selection colors are defined in a Theme instead of TextField since TextField does not expose parameters - // for overriding selectionHandleColor. - data: Theme.of(context).copyWith( - textSelectionTheme: TextSelectionThemeData( - cursorColor: style.cursor, - selectionColor: style.cursor.withOpacity(0.4), - selectionHandleColor: style.cursor, - ), - cupertinoOverrideTheme: CupertinoThemeData( - primaryColor: style.cursor, - ), - ), - // This is done because InputDecoration.errorBorder and InputDecoration.focusedErrorBorder aren't shown unless an - // additional error help text is supplied. That error help text has few configuration options. - child: ValueListenableBuilder( - valueListenable: widget.statesController ?? controller, - builder: (context, states, _) { - final (enabled, focus) = states.contains(WidgetState.error) ? (style.error, style.focusedError) : (style.enabled, style.focused); - final current = states.contains(WidgetState.focused) ? focus : enabled; - return _build(context, style, current, enabled, focus); - }, - ), - ), - ), - ], - ), - ); - - - if (materialLocalizations == null) { - return Localizations( - locale: Localizations.maybeLocaleOf(context) ?? const Locale('en', 'US'), - delegates: const [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - child: textField, - ); - } - - return textField; - } - - Widget _build( - BuildContext context, - FTextFieldStyle style, - FTextFieldStateStyle current, - FTextFieldStateStyle enabled, - FTextFieldStateStyle focused, - ) => TextField( - controller: widget.controller, - focusNode: widget.focusNode, - undoController: widget.undoController, - decoration: InputDecoration( - suffixIcon: widget.suffixIcon, - contentPadding: style.contentPadding, - hintText: widget.hint, - hintStyle: current.hint, - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: enabled.borderColor, - width: enabled.borderWidth, - ), - borderRadius: enabled.borderRadius, - ), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: style.disabled.borderColor, - width: style.disabled.borderWidth, - ), - borderRadius: style.disabled.borderRadius, - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: focused.borderColor, - width: focused.borderWidth, - ), - borderRadius: enabled.borderRadius, - ), - ), - keyboardType: widget.keyboardType, - textInputAction: widget.textInputAction, - textCapitalization: widget.textCapitalization, - style: current.content, - textAlign: widget.textAlign, - textAlignVertical: widget.textAlignVertical, - textDirection: widget.textDirection, - readOnly: widget.readOnly, - showCursor: widget.showCursor, - autofocus: widget.autofocus, - statesController: widget.statesController ?? controller, - obscureText: widget.obscureText, - autocorrect: widget.autocorrect, - smartDashesType: widget.smartDashesType, - smartQuotesType: widget.smartQuotesType, - enableSuggestions: widget.enableSuggestions, - maxLines: widget.maxLines, - minLines: widget.minLines, - expands: widget.expands, - maxLength: widget.maxLength, - maxLengthEnforcement: widget.maxLengthEnforcement, - onChanged: widget.onChange, - onEditingComplete: widget.onEditingComplete, - onSubmitted: widget.onSubmit, - onAppPrivateCommand: widget.onAppPrivateCommand, - inputFormatters: widget.inputFormatters, - enabled: widget.enabled, - ignorePointers: widget.ignorePointers, - keyboardAppearance: style.keyboardAppearance, - scrollPadding: style.scrollPadding, - dragStartBehavior: widget.dragStartBehavior, - selectionControls: widget.selectionControls, - scrollController: widget.scrollController, - scrollPhysics: widget.scrollPhysics, - autofillHints: widget.autofillHints, - restorationId: widget.restorationId, - scribbleEnabled: widget.scribbleEnabled, - enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning, - contextMenuBuilder: widget.contextMenuBuilder, - canRequestFocus: widget.canRequestFocus, - spellCheckConfiguration: widget.spellCheckConfiguration, - magnifierConfiguration: widget.magnifierConfiguration, - ); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('controller', controller)); - } - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } -} diff --git a/forui/lib/src/widgets/text_field/text_field_style.dart b/forui/lib/src/widgets/text_field/text_field_style.dart index 192ad1df9..760b45135 100644 --- a/forui/lib/src/widgets/text_field/text_field_style.dart +++ b/forui/lib/src/widgets/text_field/text_field_style.dart @@ -2,9 +2,6 @@ part of 'text_field.dart'; /// A [FTextFieldStyle]'s style. final class FTextFieldStyle with Diagnosticable { - /// The label's [TextStyle]. - final TextStyle label; - /// The appearance of the keyboard. Defaults to [FColorScheme.brightness]. /// /// This setting is only honored on iOS devices. @@ -16,7 +13,7 @@ final class FTextFieldStyle with Diagnosticable { final Color cursor; /// The padding surrounding this text field's content. Defaults to - /// `const EdgeInsets.symmetric(horizontal: 16, vertical: 7)`. + /// `const EdgeInsets.symmetric(horizontal: 15, vertical: 6)`. final EdgeInsets contentPadding; /// Configures padding to edges surrounding a [Scrollable] when this text field scrolls into view. @@ -38,73 +35,54 @@ final class FTextFieldStyle with Diagnosticable { /// The style when this text field has an error. final FTextFieldStateStyle error; - /// The style when this text field is focused. - final FTextFieldStateStyle focused; - - /// The style when this text field is focused and has an error. - final FTextFieldStateStyle focusedError; - /// Creates a [FTextFieldStyle]. FTextFieldStyle({ - required this.label, required this.keyboardAppearance, required this.enabled, required this.disabled, required this.error, - required this.focused, - required this.focusedError, this.cursor = CupertinoColors.activeBlue, - this.contentPadding = const EdgeInsets.symmetric(horizontal: 15, vertical: 7.5), + this.contentPadding = const EdgeInsets.symmetric(horizontal: 15, vertical: 5), this.scrollPadding = const EdgeInsets.all(20.0), }); /// Creates a [FTextFieldStyle] that inherits its properties. FTextFieldStyle.inherit({ required FColorScheme colorScheme, - required FFont font, + required FTypography font, required FStyle style, }): - label = TextStyle( - color: colorScheme.primary, - fontSize: font.sm, - fontWeight: FontWeight.w600, - ), keyboardAppearance = colorScheme.brightness, cursor = CupertinoColors.activeBlue, - contentPadding = const EdgeInsets.symmetric(horizontal: 15, vertical: 7.5), + contentPadding = const EdgeInsets.symmetric(horizontal: 15, vertical: 5), scrollPadding = const EdgeInsets.all(20.0), enabled = FTextFieldStateStyle.inherit( + labelColor: colorScheme.primary, contentColor: colorScheme.primary, hintColor: colorScheme.mutedForeground, - borderColor: colorScheme.border, + footerColor: colorScheme.mutedForeground, + focusedBorderColor: colorScheme.primary, + unfocusedBorderColor: colorScheme.border, font: font, style: style, ), disabled = FTextFieldStateStyle.inherit( + labelColor: colorScheme.primary, contentColor: colorScheme.primary, hintColor: colorScheme.border.withOpacity(0.5), - borderColor: colorScheme.border.withOpacity(0.5), - font: font, - style: style, - ), - error = FTextFieldStateStyle.inherit( // TODO: add error colors - contentColor: colorScheme.primary, - hintColor: colorScheme.mutedForeground, - borderColor: colorScheme.border, - font: font, - style: style, - ), - focused = FTextFieldStateStyle.inherit( - contentColor: colorScheme.primary, - hintColor: colorScheme.mutedForeground, - borderColor: colorScheme.primary, + footerColor: colorScheme.border.withOpacity(0.5), + focusedBorderColor: colorScheme.border.withOpacity(0.5), + unfocusedBorderColor: colorScheme.border.withOpacity(0.5), font: font, style: style, ), - focusedError = FTextFieldStateStyle.inherit( // TODO: add error colors + error = FTextFieldStateStyle.inherit( + labelColor: colorScheme.primary, contentColor: colorScheme.primary, hintColor: colorScheme.mutedForeground, - borderColor: colorScheme.primary, + footerColor: colorScheme.error, + focusedBorderColor: colorScheme.error, + unfocusedBorderColor: colorScheme.error, font: font, style: style, ); @@ -113,16 +91,13 @@ final class FTextFieldStyle with Diagnosticable { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('label', label)) ..add(EnumProperty('keyboardAppearance', keyboardAppearance)) ..add(ColorProperty('cursor', cursor, defaultValue: CupertinoColors.activeBlue)) ..add(DiagnosticsProperty('contentPadding', contentPadding)) ..add(DiagnosticsProperty('scrollPadding', scrollPadding)) ..add(DiagnosticsProperty('enabledBorder', enabled)) ..add(DiagnosticsProperty('disabledBorder', disabled)) - ..add(DiagnosticsProperty('errorBorder', error)) - ..add(DiagnosticsProperty('focusedBorder', focused)) - ..add(DiagnosticsProperty('focusedErrorBorder', focusedError)); + ..add(DiagnosticsProperty('errorBorder', error)); } @override @@ -130,87 +105,99 @@ final class FTextFieldStyle with Diagnosticable { identical(this, other) || other is FTextFieldStyle && runtimeType == other.runtimeType && - label == other.label && keyboardAppearance == other.keyboardAppearance && cursor == other.cursor && contentPadding == other.contentPadding && scrollPadding == other.scrollPadding && enabled == other.enabled && disabled == other.disabled && - error == other.error && - focused == other.focused && - focusedError == other.focusedError; + error == other.error; @override int get hashCode => - label.hashCode ^ keyboardAppearance.hashCode ^ cursor.hashCode ^ contentPadding.hashCode ^ scrollPadding.hashCode ^ enabled.hashCode ^ disabled.hashCode ^ - error.hashCode ^ - focused.hashCode ^ - focusedError.hashCode; + error.hashCode; } /// A [FTextFieldStateStyle]'s style. final class FTextFieldStateStyle with Diagnosticable { + /// The label's [TextStyle]. + final TextStyle label; + /// The content's [TextStyle]. final TextStyle content; /// The hint's [TextStyle]. final TextStyle hint; - /// The border's color. - final Color borderColor; + /// The help/error's [TextStyle]. + final TextStyle footer; - /// The border's width. Defaults to [FStyle.borderWidth]. - final double borderWidth; + /// The border's color when focused. + final FTextFieldBorderStyle focused; - /// The border's width. Defaults to [FStyle.borderRadius]. - final BorderRadius borderRadius; + /// The border's style when unfocused. + final FTextFieldBorderStyle unfocused; /// Creates a [FTextFieldStateStyle]. FTextFieldStateStyle({ + required this.label, required this.content, required this.hint, - required this.borderColor, - required this.borderWidth, - required this.borderRadius, + required this.footer, + required this.focused, + required this.unfocused, }); /// Creates a [FTextFieldStateStyle] that inherits its properties. FTextFieldStateStyle.inherit({ + required Color labelColor, required Color contentColor, required Color hintColor, - required this.borderColor, - required FFont font, + required Color footerColor, + required Color focusedBorderColor, + required Color unfocusedBorderColor, + required FTypography font, required FStyle style, }): + label = TextStyle( + color: labelColor, + fontSize: font.sm, + fontWeight: FontWeight.w600, + ), content = TextStyle( - fontFamily: font.family, + fontFamily: font.defaultFontFamily, fontSize: font.sm, color: contentColor, ), hint = TextStyle( - fontFamily: font.family, + fontFamily: font.defaultFontFamily, fontSize: font.sm, color: hintColor, ), - borderWidth = style.borderWidth, - borderRadius = style.borderRadius; + footer = TextStyle( + fontFamily: font.defaultFontFamily, + fontSize: font.sm, + color: footerColor, + ), + focused = FTextFieldBorderStyle.inherit(color: focusedBorderColor, style: style), + unfocused = FTextFieldBorderStyle.inherit(color: unfocusedBorderColor, style: style); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties + ..add(DiagnosticsProperty('label', label)) ..add(DiagnosticsProperty('content', content)) ..add(DiagnosticsProperty('hint', hint)) - ..add(ColorProperty('borderColor', borderColor)) - ..add(DoubleProperty('borderWidth', borderWidth)) - ..add(DiagnosticsProperty('borderRadius', borderRadius)); + ..add(DiagnosticsProperty('footer', footer)) + ..add(DiagnosticsProperty('focused', focused)) + ..add(DiagnosticsProperty('unfocused', unfocused)); } @override @@ -218,17 +205,76 @@ final class FTextFieldStateStyle with Diagnosticable { identical(this, other) || other is FTextFieldStateStyle && runtimeType == other.runtimeType && + label == other.label && content == other.content && hint == other.hint && - borderColor == other.borderColor && - borderWidth == other.borderWidth && - borderRadius == other.borderRadius; + focused == other.focused && + unfocused == other.unfocused; @override int get hashCode => + label.hashCode ^ content.hashCode ^ hint.hashCode ^ - borderColor.hashCode ^ - borderWidth.hashCode ^ - borderRadius.hashCode; + focused.hashCode ^ + unfocused.hashCode; +} + +/// A [FTextFieldBorderStyle]'s style. +final class FTextFieldBorderStyle with Diagnosticable { + /// The border's color. + final Color color; + + /// The border's width. Defaults to [FStyle.borderWidth]. + final double width; + + /// The border's width. Defaults to [FStyle.borderRadius]. + final BorderRadius radius; + + /// Creates a [FTextFieldBorderStyle]. + FTextFieldBorderStyle({ + required this.color, + required this.width, + required this.radius, + }); + + /// Creates a [FTextFieldBorderStyle] that inherits its properties. + FTextFieldBorderStyle.inherit({ + required this.color, + required FStyle style, + }): + width = style.borderWidth, + radius = style.borderRadius; + + /// Creates a copy of this border style but with the given fields replaced with the new values. + FTextFieldBorderStyle copyWith({ + Color? color, + double? width, + BorderRadius? radius, + }) => FTextFieldBorderStyle( + color: color ?? this.color, + width: width ?? this.width, + radius: radius ?? this.radius, + ); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(ColorProperty('color', color)) + ..add(DoubleProperty('width', width)) + ..add(DiagnosticsProperty('radius', radius)); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is FTextFieldBorderStyle && + runtimeType == other.runtimeType && + color == other.color && + width == other.width && + radius == other.radius; + + @override + int get hashCode => color.hashCode ^ width.hashCode ^ radius.hashCode; } diff --git a/forui/test/golden/text_field/default-zinc-dark-focused-no-text.png b/forui/test/golden/text_field/default-zinc-dark-focused-no-text.png index 8ae340b82..b8d3538b6 100644 Binary files a/forui/test/golden/text_field/default-zinc-dark-focused-no-text.png and b/forui/test/golden/text_field/default-zinc-dark-focused-no-text.png differ diff --git a/forui/test/golden/text_field/default-zinc-dark-focused-text.png b/forui/test/golden/text_field/default-zinc-dark-focused-text.png index 940816ed4..ebdd25dba 100644 Binary files a/forui/test/golden/text_field/default-zinc-dark-focused-text.png and b/forui/test/golden/text_field/default-zinc-dark-focused-text.png differ diff --git a/forui/test/golden/text_field/default-zinc-dark-unfocused-no-text.png b/forui/test/golden/text_field/default-zinc-dark-unfocused-no-text.png index 61a52f5bb..e1199aa57 100644 Binary files a/forui/test/golden/text_field/default-zinc-dark-unfocused-no-text.png and b/forui/test/golden/text_field/default-zinc-dark-unfocused-no-text.png differ diff --git a/forui/test/golden/text_field/default-zinc-dark-unfocused-text.png b/forui/test/golden/text_field/default-zinc-dark-unfocused-text.png index fa7145324..3411431a4 100644 Binary files a/forui/test/golden/text_field/default-zinc-dark-unfocused-text.png and b/forui/test/golden/text_field/default-zinc-dark-unfocused-text.png differ diff --git a/forui/test/golden/text_field/default-zinc-light-focused-no-text.png b/forui/test/golden/text_field/default-zinc-light-focused-no-text.png index 28d6413d1..f13e6beeb 100644 Binary files a/forui/test/golden/text_field/default-zinc-light-focused-no-text.png and b/forui/test/golden/text_field/default-zinc-light-focused-no-text.png differ diff --git a/forui/test/golden/text_field/default-zinc-light-focused-text.png b/forui/test/golden/text_field/default-zinc-light-focused-text.png index 4557fa8a6..6d84c5337 100644 Binary files a/forui/test/golden/text_field/default-zinc-light-focused-text.png and b/forui/test/golden/text_field/default-zinc-light-focused-text.png differ diff --git a/forui/test/golden/text_field/default-zinc-light-unfocused-no-text.png b/forui/test/golden/text_field/default-zinc-light-unfocused-no-text.png index 9361dea9a..19eaef775 100644 Binary files a/forui/test/golden/text_field/default-zinc-light-unfocused-no-text.png and b/forui/test/golden/text_field/default-zinc-light-unfocused-no-text.png differ diff --git a/forui/test/golden/text_field/default-zinc-light-unfocused-text.png b/forui/test/golden/text_field/default-zinc-light-unfocused-text.png index 108185b6e..a186a28e4 100644 Binary files a/forui/test/golden/text_field/default-zinc-light-unfocused-text.png and b/forui/test/golden/text_field/default-zinc-light-unfocused-text.png differ diff --git a/forui/test/golden/text_field/email-zinc-dark-focused-no-text.png b/forui/test/golden/text_field/email-zinc-dark-focused-no-text.png index 9fa726362..ab3c651b0 100644 Binary files a/forui/test/golden/text_field/email-zinc-dark-focused-no-text.png and b/forui/test/golden/text_field/email-zinc-dark-focused-no-text.png differ diff --git a/forui/test/golden/text_field/email-zinc-dark-focused-text.png b/forui/test/golden/text_field/email-zinc-dark-focused-text.png index 62998c294..74b8332ac 100644 Binary files a/forui/test/golden/text_field/email-zinc-dark-focused-text.png and b/forui/test/golden/text_field/email-zinc-dark-focused-text.png differ diff --git a/forui/test/golden/text_field/email-zinc-dark-unfocused-no-text.png b/forui/test/golden/text_field/email-zinc-dark-unfocused-no-text.png index 486a4deba..015e28748 100644 Binary files a/forui/test/golden/text_field/email-zinc-dark-unfocused-no-text.png and b/forui/test/golden/text_field/email-zinc-dark-unfocused-no-text.png differ diff --git a/forui/test/golden/text_field/email-zinc-dark-unfocused-text.png b/forui/test/golden/text_field/email-zinc-dark-unfocused-text.png index 55cf8ba2e..afed1d0f4 100644 Binary files a/forui/test/golden/text_field/email-zinc-dark-unfocused-text.png and b/forui/test/golden/text_field/email-zinc-dark-unfocused-text.png differ diff --git a/forui/test/golden/text_field/email-zinc-light-focused-no-text.png b/forui/test/golden/text_field/email-zinc-light-focused-no-text.png index 9e8865a27..fc8a3c859 100644 Binary files a/forui/test/golden/text_field/email-zinc-light-focused-no-text.png and b/forui/test/golden/text_field/email-zinc-light-focused-no-text.png differ diff --git a/forui/test/golden/text_field/email-zinc-light-focused-text.png b/forui/test/golden/text_field/email-zinc-light-focused-text.png index 7eb8d7f16..bdff36501 100644 Binary files a/forui/test/golden/text_field/email-zinc-light-focused-text.png and b/forui/test/golden/text_field/email-zinc-light-focused-text.png differ diff --git a/forui/test/golden/text_field/email-zinc-light-unfocused-no-text.png b/forui/test/golden/text_field/email-zinc-light-unfocused-no-text.png index 733056d6b..19c21dfb9 100644 Binary files a/forui/test/golden/text_field/email-zinc-light-unfocused-no-text.png and b/forui/test/golden/text_field/email-zinc-light-unfocused-no-text.png differ diff --git a/forui/test/golden/text_field/email-zinc-light-unfocused-text.png b/forui/test/golden/text_field/email-zinc-light-unfocused-text.png index c73729262..b62d9e506 100644 Binary files a/forui/test/golden/text_field/email-zinc-light-unfocused-text.png and b/forui/test/golden/text_field/email-zinc-light-unfocused-text.png differ diff --git a/forui/test/golden/text_field/error-zinc-dark-focused-no-text.png b/forui/test/golden/text_field/error-zinc-dark-focused-no-text.png new file mode 100644 index 000000000..a5854f0c4 Binary files /dev/null and b/forui/test/golden/text_field/error-zinc-dark-focused-no-text.png differ diff --git a/forui/test/golden/text_field/error-zinc-dark-focused-text.png b/forui/test/golden/text_field/error-zinc-dark-focused-text.png new file mode 100644 index 000000000..af50450f8 Binary files /dev/null and b/forui/test/golden/text_field/error-zinc-dark-focused-text.png differ diff --git a/forui/test/golden/text_field/error-zinc-dark-unfocused-no-text.png b/forui/test/golden/text_field/error-zinc-dark-unfocused-no-text.png new file mode 100644 index 000000000..5e41fa604 Binary files /dev/null and b/forui/test/golden/text_field/error-zinc-dark-unfocused-no-text.png differ diff --git a/forui/test/golden/text_field/error-zinc-dark-unfocused-text.png b/forui/test/golden/text_field/error-zinc-dark-unfocused-text.png new file mode 100644 index 000000000..936a868c3 Binary files /dev/null and b/forui/test/golden/text_field/error-zinc-dark-unfocused-text.png differ diff --git a/forui/test/golden/text_field/error-zinc-light-focused-no-text.png b/forui/test/golden/text_field/error-zinc-light-focused-no-text.png new file mode 100644 index 000000000..cead17e16 Binary files /dev/null and b/forui/test/golden/text_field/error-zinc-light-focused-no-text.png differ diff --git a/forui/test/golden/text_field/error-zinc-light-focused-text.png b/forui/test/golden/text_field/error-zinc-light-focused-text.png new file mode 100644 index 000000000..446166722 Binary files /dev/null and b/forui/test/golden/text_field/error-zinc-light-focused-text.png differ diff --git a/forui/test/golden/text_field/error-zinc-light-unfocused-no-text.png b/forui/test/golden/text_field/error-zinc-light-unfocused-no-text.png new file mode 100644 index 000000000..7aa78de0d Binary files /dev/null and b/forui/test/golden/text_field/error-zinc-light-unfocused-no-text.png differ diff --git a/forui/test/golden/text_field/error-zinc-light-unfocused-text.png b/forui/test/golden/text_field/error-zinc-light-unfocused-text.png new file mode 100644 index 000000000..c57b9628a Binary files /dev/null and b/forui/test/golden/text_field/error-zinc-light-unfocused-text.png differ diff --git a/forui/test/golden/text_field/multiline-zinc-dark-focused-no-text.png b/forui/test/golden/text_field/multiline-zinc-dark-focused-no-text.png index 5c79fb8f9..53837cd5c 100644 Binary files a/forui/test/golden/text_field/multiline-zinc-dark-focused-no-text.png and b/forui/test/golden/text_field/multiline-zinc-dark-focused-no-text.png differ diff --git a/forui/test/golden/text_field/multiline-zinc-dark-focused-text.png b/forui/test/golden/text_field/multiline-zinc-dark-focused-text.png index 05ca6be60..e6729286c 100644 Binary files a/forui/test/golden/text_field/multiline-zinc-dark-focused-text.png and b/forui/test/golden/text_field/multiline-zinc-dark-focused-text.png differ diff --git a/forui/test/golden/text_field/multiline-zinc-dark-unfocused-no-text.png b/forui/test/golden/text_field/multiline-zinc-dark-unfocused-no-text.png index 6b4ddc70c..4a0f9f02f 100644 Binary files a/forui/test/golden/text_field/multiline-zinc-dark-unfocused-no-text.png and b/forui/test/golden/text_field/multiline-zinc-dark-unfocused-no-text.png differ diff --git a/forui/test/golden/text_field/multiline-zinc-dark-unfocused-text.png b/forui/test/golden/text_field/multiline-zinc-dark-unfocused-text.png index d6e73455a..62fc506a7 100644 Binary files a/forui/test/golden/text_field/multiline-zinc-dark-unfocused-text.png and b/forui/test/golden/text_field/multiline-zinc-dark-unfocused-text.png differ diff --git a/forui/test/golden/text_field/multiline-zinc-light-focused-no-text.png b/forui/test/golden/text_field/multiline-zinc-light-focused-no-text.png index de489c597..75b2cec59 100644 Binary files a/forui/test/golden/text_field/multiline-zinc-light-focused-no-text.png and b/forui/test/golden/text_field/multiline-zinc-light-focused-no-text.png differ diff --git a/forui/test/golden/text_field/multiline-zinc-light-focused-text.png b/forui/test/golden/text_field/multiline-zinc-light-focused-text.png index a291a7a34..0116cb435 100644 Binary files a/forui/test/golden/text_field/multiline-zinc-light-focused-text.png and b/forui/test/golden/text_field/multiline-zinc-light-focused-text.png differ diff --git a/forui/test/golden/text_field/multiline-zinc-light-unfocused-no-text.png b/forui/test/golden/text_field/multiline-zinc-light-unfocused-no-text.png index c3ba87d33..c2898c00d 100644 Binary files a/forui/test/golden/text_field/multiline-zinc-light-unfocused-no-text.png and b/forui/test/golden/text_field/multiline-zinc-light-unfocused-no-text.png differ diff --git a/forui/test/golden/text_field/multiline-zinc-light-unfocused-text.png b/forui/test/golden/text_field/multiline-zinc-light-unfocused-text.png index 1a64fde14..52831c15c 100644 Binary files a/forui/test/golden/text_field/multiline-zinc-light-unfocused-text.png and b/forui/test/golden/text_field/multiline-zinc-light-unfocused-text.png differ diff --git a/forui/test/golden/text_field/password-zinc-dark-focused-no-text.png b/forui/test/golden/text_field/password-zinc-dark-focused-no-text.png index 93b2f8d5b..6119a8b14 100644 Binary files a/forui/test/golden/text_field/password-zinc-dark-focused-no-text.png and b/forui/test/golden/text_field/password-zinc-dark-focused-no-text.png differ diff --git a/forui/test/golden/text_field/password-zinc-dark-focused-text.png b/forui/test/golden/text_field/password-zinc-dark-focused-text.png index beada02a5..3cf839085 100644 Binary files a/forui/test/golden/text_field/password-zinc-dark-focused-text.png and b/forui/test/golden/text_field/password-zinc-dark-focused-text.png differ diff --git a/forui/test/golden/text_field/password-zinc-dark-unfocused-no-text.png b/forui/test/golden/text_field/password-zinc-dark-unfocused-no-text.png index f83335c36..83888c8c0 100644 Binary files a/forui/test/golden/text_field/password-zinc-dark-unfocused-no-text.png and b/forui/test/golden/text_field/password-zinc-dark-unfocused-no-text.png differ diff --git a/forui/test/golden/text_field/password-zinc-dark-unfocused-text.png b/forui/test/golden/text_field/password-zinc-dark-unfocused-text.png index 5f702f2bf..9f19b3507 100644 Binary files a/forui/test/golden/text_field/password-zinc-dark-unfocused-text.png and b/forui/test/golden/text_field/password-zinc-dark-unfocused-text.png differ diff --git a/forui/test/golden/text_field/password-zinc-light-focused-no-text.png b/forui/test/golden/text_field/password-zinc-light-focused-no-text.png index b1bc7c77a..e02f64e1f 100644 Binary files a/forui/test/golden/text_field/password-zinc-light-focused-no-text.png and b/forui/test/golden/text_field/password-zinc-light-focused-no-text.png differ diff --git a/forui/test/golden/text_field/password-zinc-light-focused-text.png b/forui/test/golden/text_field/password-zinc-light-focused-text.png index 33d017d4d..903271a23 100644 Binary files a/forui/test/golden/text_field/password-zinc-light-focused-text.png and b/forui/test/golden/text_field/password-zinc-light-focused-text.png differ diff --git a/forui/test/golden/text_field/password-zinc-light-unfocused-no-text.png b/forui/test/golden/text_field/password-zinc-light-unfocused-no-text.png index 3e730818f..264a6232d 100644 Binary files a/forui/test/golden/text_field/password-zinc-light-unfocused-no-text.png and b/forui/test/golden/text_field/password-zinc-light-unfocused-no-text.png differ diff --git a/forui/test/golden/text_field/password-zinc-light-unfocused-text.png b/forui/test/golden/text_field/password-zinc-light-unfocused-text.png index 2f5c6b181..99803066b 100644 Binary files a/forui/test/golden/text_field/password-zinc-light-unfocused-text.png and b/forui/test/golden/text_field/password-zinc-light-unfocused-text.png differ diff --git a/forui/test/src/theme/color_scheme_test.dart b/forui/test/src/theme/color_scheme_test.dart index 9f5a683aa..35817ea9c 100644 --- a/forui/test/src/theme/color_scheme_test.dart +++ b/forui/test/src/theme/color_scheme_test.dart @@ -19,6 +19,8 @@ void main() { mutedForeground: Colors.blue, destructive: Colors.blueAccent, destructiveForeground: Colors.blueGrey, + error: Colors.red, + errorForeground: Colors.redAccent, border: Colors.lightBlue, ); @@ -38,6 +40,8 @@ void main() { mutedForeground: Colors.indigo, destructive: Colors.teal, destructiveForeground: Colors.pink, + error: Colors.blueAccent, + errorForeground: Colors.blueGrey, border: Colors.lime, ); @@ -52,6 +56,8 @@ void main() { expect(copy.mutedForeground, equals(Colors.indigo)); expect(copy.destructive, equals(Colors.teal)); expect(copy.destructiveForeground, equals(Colors.pink)); + expect(copy.error, equals(Colors.blueAccent)); + expect(copy.errorForeground, equals(Colors.blueGrey)); expect(copy.border, equals(Colors.lime)); }); }); @@ -72,6 +78,8 @@ void main() { ColorProperty('mutedForeground', Colors.blue), ColorProperty('destructive', Colors.blueAccent), ColorProperty('destructiveForeground', Colors.blueGrey), + ColorProperty('error', Colors.red), + ColorProperty('errorForeground', Colors.redAccent), ColorProperty('border', Colors.lightBlue), ].map((p) => p.toString())); }); diff --git a/forui/test/src/theme/font_test.dart b/forui/test/src/theme/typoegraphy_test.dart similarity index 59% rename from forui/test/src/theme/font_test.dart rename to forui/test/src/theme/typoegraphy_test.dart index 4822c990c..243d46256 100644 --- a/forui/test/src/theme/font_test.dart +++ b/forui/test/src/theme/typoegraphy_test.dart @@ -6,9 +6,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:forui/forui.dart'; void main() { - group('FFont', () { - final font = FFont( - family: 'Roboto', + group('FTypography', () { + final typography = FTypography( + defaultFontFamily: 'Roboto', sizeScalar: 2, letterSpacingScalar: 3, wordSpacingScalar: 4, @@ -17,55 +17,55 @@ void main() { group('constructor', () { test('no arguments', () { - final font = FFont(); - expect(font.family, 'packages/forui/Inter'); - expect(font.sizeScalar, 1); - expect(font.letterSpacingScalar, 1); - expect(font.wordSpacingScalar, 1); - expect(font.heightScalar, 1); + final typography = FTypography(); + expect(typography.defaultFontFamily, 'packages/forui/Inter'); + expect(typography.sizeScalar, 1); + expect(typography.letterSpacingScalar, 1); + expect(typography.wordSpacingScalar, 1); + expect(typography.heightScalar, 1); }); - test('blank font family', () => expect(() => FFont(family: ''), throwsAssertionError)); + test('blank font family', () => expect(() => FTypography(defaultFontFamily: ''), throwsAssertionError)); - test('sizeScalar is 0', () => expect(() => FFont(sizeScalar: 0), throwsAssertionError)); + test('sizeScalar is 0', () => expect(() => FTypography(sizeScalar: 0), throwsAssertionError)); - test('sizeScalar is NaN', () => expect(() => FFont(sizeScalar: double.nan), throwsAssertionError)); + test('sizeScalar is NaN', () => expect(() => FTypography(sizeScalar: double.nan), throwsAssertionError)); - test('letterSpacingScalar is 0', () => expect(() => FFont(letterSpacingScalar: 0), throwsAssertionError)); + test('letterSpacingScalar is 0', () => expect(() => FTypography(letterSpacingScalar: 0), throwsAssertionError)); test('letterSpacingScalar is NaN', - () => expect(() => FFont(letterSpacingScalar: double.nan), throwsAssertionError)); + () => expect(() => FTypography(letterSpacingScalar: double.nan), throwsAssertionError)); - test('wordSpacingScalar is 0', () => expect(() => FFont(wordSpacingScalar: 0), throwsAssertionError)); + test('wordSpacingScalar is 0', () => expect(() => FTypography(wordSpacingScalar: 0), throwsAssertionError)); - test('wordSpacingScalar is NaN', () => expect(() => FFont(wordSpacingScalar: double.nan), throwsAssertionError)); + test('wordSpacingScalar is NaN', () => expect(() => FTypography(wordSpacingScalar: double.nan), throwsAssertionError)); - test('heightScalar is 0', () => expect(() => FFont(heightScalar: 0), throwsAssertionError)); + test('heightScalar is 0', () => expect(() => FTypography(heightScalar: 0), throwsAssertionError)); - test('heightScalar is NaN', () => expect(() => FFont(heightScalar: double.nan), throwsAssertionError)); + test('heightScalar is NaN', () => expect(() => FTypography(heightScalar: double.nan), throwsAssertionError)); }); group('copyWith(...)', () { test('no arguments', () { - font.copyWith(); + typography.copyWith(); - expect(font.family, 'Roboto'); - expect(font.sizeScalar, 2); - expect(font.letterSpacingScalar, 3); - expect(font.wordSpacingScalar, 4); - expect(font.heightScalar, 5); + expect(typography.defaultFontFamily, 'Roboto'); + expect(typography.sizeScalar, 2); + expect(typography.letterSpacingScalar, 3); + expect(typography.wordSpacingScalar, 4); + expect(typography.heightScalar, 5); }); test('all arguments', () { - final font = FFont().copyWith( - family: 'Roboto', + final font = FTypography().copyWith( + defaultFontFamily: 'Roboto', sizeScalar: 2, letterSpacingScalar: 3, wordSpacingScalar: 4, heightScalar: 5, ); - expect(font.family, 'Roboto'); + expect(font.defaultFontFamily, 'Roboto'); expect(font.sizeScalar, 2); expect(font.letterSpacingScalar, 3); expect(font.wordSpacingScalar, 4); @@ -75,7 +75,7 @@ void main() { group('toTextStyle(...)', () { test('no arguments', () { - final style = font.toTextStyle(); + final style = typography.toTextStyle(); expect(style.fontFamily, 'Roboto'); expect(style.fontSize, null); @@ -85,7 +85,7 @@ void main() { }); test('scaled arguments', () { - final style = font.toTextStyle( + final style = typography.toTextStyle( fontSize: 7, letterSpacing: 11, wordSpacing: 13, @@ -100,7 +100,7 @@ void main() { }); test('other arguments', () { - final style = font.toTextStyle( + final style = typography.toTextStyle( color: const Color(0xFF000000), ); @@ -109,8 +109,8 @@ void main() { }); test('debugFillProperties', () { - final font = FFont( - family: 'Roboto', + final font = FTypography( + defaultFontFamily: 'Roboto', sizeScalar: 2, letterSpacingScalar: 3, wordSpacingScalar: 4, @@ -157,29 +157,29 @@ void main() { group('equality and hashcode', () { test('equal', () { - final copy = font.copyWith(); - expect(copy, font); - expect(copy.hashCode, font.hashCode); + final copy = typography.copyWith(); + expect(copy, typography); + expect(copy.hashCode, typography.hashCode); }); test('not equal', () { - final copy = font.copyWith(family: 'Else'); - expect(copy, isNot(font)); - expect(copy.hashCode, isNot(font.hashCode)); + final copy = typography.copyWith(defaultFontFamily: 'Else'); + expect(copy, isNot(typography)); + expect(copy.hashCode, isNot(typography.hashCode)); }); }); }); group('FontTextStyle', () { - final font = FFont( - family: 'Roboto', + final font = FTypography( + defaultFontFamily: 'Roboto', sizeScalar: 2, letterSpacingScalar: 3, wordSpacingScalar: 4, heightScalar: 5, ); - test('withFont(...)', () { + test('scale(...)', () { final style = const TextStyle( color: Colors.blueAccent, fontFamily: 'default-font', @@ -187,10 +187,10 @@ void main() { letterSpacing: 11, wordSpacing: 13, height: 17, - ).withFont(font); + ).scale(font); expect(style.color, Colors.blueAccent); - expect(style.fontFamily, 'Roboto'); + expect(style.fontFamily, 'default-font'); expect(style.fontSize, 14); expect(style.letterSpacing, 33); expect(style.wordSpacing, 52); diff --git a/forui/test/src/widgets/text_field/text_field_golden_test.dart b/forui/test/src/widgets/text_field/text_field_golden_test.dart index 09919642c..8aae0f907 100644 --- a/forui/test/src/widgets/text_field/text_field_golden_test.dart +++ b/forui/test/src/widgets/text_field/text_field_golden_test.dart @@ -30,8 +30,9 @@ void main() { child: FTextField( controller: controller, autofocus: focused_, - label: 'My Label', - hint: 'hint', + labelText: 'My Label', + hintText: 'hint', + helpText: 'Some help text.', ), ), ), @@ -46,6 +47,34 @@ void main() { ); }); + testWidgets('error - $theme - $focused - $text', (tester) async { + final controller = text_ == null ? null : TextEditingController(text: text_); + await tester.pumpWidget( + MaterialApp( + home: TestScaffold( + data: theme_, + child: Padding( + padding: const EdgeInsets.all(20), + child: FTextField( + controller: controller, + autofocus: focused_, + labelText: 'My Label', + hintText: 'hint', + errorText: 'An error has occurred.', + ), + ), + ), + ), + ); + + await tester.pumpAndSettle(); + + await expectLater( + find.byType(TestScaffold), + matchesGoldenFile('text_field/error-$theme-$focused-$text.png'), + ); + }); + testWidgets('email - $theme - $focused - $text', (tester) async { final controller = text_ == null ? null : TextEditingController(text: text_); await tester.pumpWidget( @@ -57,8 +86,8 @@ void main() { child: FTextField.email( controller: controller, autofocus: focused_, - label: 'Email', - hint: 'janedoe@foruslabs.com', + labelText: 'Email', + hintText: 'janedoe@foruslabs.com', ), ), ), @@ -84,8 +113,8 @@ void main() { child: FTextField.password( controller: controller, autofocus: focused_, - label: 'Password', - hint: 'password', + labelText: 'Password', + hintText: 'password', ), ), ), @@ -113,8 +142,8 @@ void main() { child: FTextField.multiline( controller: controller, autofocus: focused_, - label: 'My Label', - hint: 'hint', + labelText: 'My Label', + hintText: 'hint', ), ), ),