From 5dd0b57544a5dfa1ef6de82f13f42d6f1db4af00 Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 16 Aug 2024 20:39:37 +0800 Subject: [PATCH] Complete implementation --- forui/CHANGELOG.md | 2 + forui/example/ios/Runner/AppDelegate.swift | 2 +- forui/example/lib/main.dart | 2 +- forui/example/lib/sandbox.dart | 30 ++- forui/example/pubspec.lock | 24 +- .../src/foundation/stateless_form_field.dart | 93 +++++++ forui/lib/src/theme/form_field_style.dart | 4 +- forui/lib/src/theme/theme_data.dart | 1 - forui/lib/src/widgets/checkbox.dart | 244 ++++++++---------- forui/lib/src/widgets/label.dart | 14 +- samples/pubspec.lock | 24 +- 11 files changed, 265 insertions(+), 175 deletions(-) create mode 100644 forui/lib/src/foundation/stateless_form_field.dart diff --git a/forui/CHANGELOG.md b/forui/CHANGELOG.md index 59108aa5c..e7909d5c0 100644 --- a/forui/CHANGELOG.md +++ b/forui/CHANGELOG.md @@ -11,6 +11,8 @@ * Fix `FTextField` error message replacing the description text. +* Fix `FCheckboxStyle.inherit` icon color inheriting from the wrong color. + ## 0.5.0 diff --git a/forui/example/ios/Runner/AppDelegate.swift b/forui/example/ios/Runner/AppDelegate.swift index 70693e4a8..b63630348 100644 --- a/forui/example/ios/Runner/AppDelegate.swift +++ b/forui/example/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/forui/example/lib/main.dart b/forui/example/lib/main.dart index 757dd5fdd..4f98061d2 100644 --- a/forui/example/lib/main.dart +++ b/forui/example/lib/main.dart @@ -24,7 +24,7 @@ class Application extends StatefulWidget { } class _ApplicationState extends State { - int index = 1; + int index = 0; @override Widget build(BuildContext context) => MaterialApp( diff --git a/forui/example/lib/sandbox.dart b/forui/example/lib/sandbox.dart index 9c80e0c7a..d99ea1917 100644 --- a/forui/example/lib/sandbox.dart +++ b/forui/example/lib/sandbox.dart @@ -18,11 +18,37 @@ class _SandboxState extends State { @override Widget build(BuildContext context) => ListView( padding: EdgeInsets.zero, - children: [ + children: const [ FCheckbox( label: Text('Remember me'), description: Text('Remember me on this device.'), - initialValue: true, + // forceErrorText: 'Please check the box to continue.', + // initialValue: true, + enabled: false, + ), + SizedBox(height: 20), + FLabel( + axis: Axis.vertical, + label: Text('Email'), + description: Text('Enter your email address.'), + error: Text('Please enter a valid email address.'), + state: FLabelState.error, + child: DecoratedBox( + decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5)), color: Colors.grey), + child: SizedBox(width: 200, height: 30), + ), + ), + SizedBox(height: 20), + FLabel( + axis: Axis.horizontal, + label: Text('Email'), + description: Text('Enter your email address.'), + error: Text('Please enter a valid email address.'), + state: FLabelState.error, + child: DecoratedBox( + decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5)), color: Colors.grey), + child: SizedBox(width: 16, height: 16), + ), ), ], ); diff --git a/forui/example/pubspec.lock b/forui/example/pubspec.lock index 3cd5842b6..e522f27d0 100644 --- a/forui/example/pubspec.lock +++ b/forui/example/pubspec.lock @@ -338,18 +338,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -378,18 +378,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mime: dependency: transitive description: @@ -631,10 +631,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" timing: dependency: transitive description: @@ -687,10 +687,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.4" watcher: dependency: transitive description: diff --git a/forui/lib/src/foundation/stateless_form_field.dart b/forui/lib/src/foundation/stateless_form_field.dart new file mode 100644 index 000000000..42ec34342 --- /dev/null +++ b/forui/lib/src/foundation/stateless_form_field.dart @@ -0,0 +1,93 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +/// A stateless single form field. +/// +/// This widget is meant to be extended by other form field widgets. +abstract class FStatelessFormField extends StatelessWidget { + /// An optional method to call with the final value when the form is saved via [FormState.save]. + final FormFieldSetter? onSave; + + /// An optional property that forces the [FormFieldState] into an error state by directly setting the + /// [FormFieldState.errorText] property without running the validator function. + /// + /// When the [forceErrorText] property is provided, the [FormFieldState.errorText] will be set to the provided value, + /// causing the form field to be considered invalid and to display the error message specified. + /// + /// When [validator] is provided, [forceErrorText] will override any error that it returns. [validator] will not be + /// called unless [forceErrorText] is null. + final String? forceErrorText; + + /// An optional method that validates an input. Returns an error string to display if the input is invalid, or null + /// otherwise. + /// + /// The returned value is exposed by the [FormFieldState.errorText] property. + final FormFieldValidator? validator; + + /// An optional value to initialize the form field to, or null otherwise. + final T initialValue; + + /// Whether the form is able to receive user input. + /// + /// Defaults to true. If [autovalidateMode] is not [AutovalidateMode.disabled], the checkbox will be auto validated. + /// Likewise, if this field is false, the widget will not be validated regardless of [autovalidateMode]. + final bool enabled; + + /// Used to enable/disable this checkbox auto validation and update its error text. + /// + /// Defaults to [AutovalidateMode.disabled]. + /// + /// If [AutovalidateMode.onUserInteraction], this checkbox will only auto-validate after its content changes. If + /// [AutovalidateMode.always], it will auto-validate even without user interaction. If [AutovalidateMode.disabled], + /// auto-validation will be disabled. + final AutovalidateMode autovalidateMode; + + /// Restoration ID to save and restore the state of the form field. + /// + /// Setting the restoration ID to a non-null value results in whether or not the form field validation persists. + /// + /// The state of this widget is persisted in a [RestorationBucket] claimed from the surrounding [RestorationScope] + /// using the provided restoration ID. + /// + /// See also: + /// * [RestorationManager], which explains how state restoration works in Flutter. + final String? restorationId; + + const FStatelessFormField({ + required this.initialValue, + this.onSave, + this.forceErrorText, + this.validator, + this.enabled = true, + this.autovalidateMode = AutovalidateMode.disabled, + this.restorationId, + super.key, + }); + + @override + Widget build(BuildContext context) => FormField( + onSaved: onSave, + forceErrorText: forceErrorText, + validator: validator, + initialValue: initialValue, + enabled: enabled, + autovalidateMode: autovalidateMode, + restorationId: restorationId, + builder: (state) => builder(context, state), + ); + + Widget builder(BuildContext context, FormFieldState state); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(ObjectFlagProperty.has('onSave', onSave)) + ..add(StringProperty('forceErrorText', forceErrorText)) + ..add(ObjectFlagProperty.has('validator', validator)) + ..add(DiagnosticsProperty('initialValue', initialValue)) + ..add(DiagnosticsProperty('enabled', enabled)) + ..add(EnumProperty('autovalidateMode', autovalidateMode)) + ..add(StringProperty('restorationId', restorationId)); + } +} diff --git a/forui/lib/src/theme/form_field_style.dart b/forui/lib/src/theme/form_field_style.dart index e4b833428..ed2a82289 100644 --- a/forui/lib/src/theme/form_field_style.dart +++ b/forui/lib/src/theme/form_field_style.dart @@ -34,11 +34,11 @@ final class FFormFieldStyle with Diagnosticable { ), disabledStyle = FFormFieldNormalStyle.inherit( labelColor: colorScheme.primary.withOpacity(0.7), - descriptionColor: colorScheme.border.withOpacity(0.7), + descriptionColor: colorScheme.mutedForeground.withOpacity(0.7), typography: typography, ), errorStyle = FFormFieldErrorStyle.inherit( - labelColor: colorScheme.primary, + labelColor: colorScheme.error, descriptionColor: colorScheme.mutedForeground, errorColor: colorScheme.error, typography: typography, diff --git a/forui/lib/src/theme/theme_data.dart b/forui/lib/src/theme/theme_data.dart index afc168ba6..6c4c311f7 100644 --- a/forui/lib/src/theme/theme_data.dart +++ b/forui/lib/src/theme/theme_data.dart @@ -1,6 +1,5 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; -import 'package:forui/widgets/label.dart'; import 'package:meta/meta.dart'; diff --git a/forui/lib/src/widgets/checkbox.dart b/forui/lib/src/widgets/checkbox.dart index 985255401..c05506576 100644 --- a/forui/lib/src/widgets/checkbox.dart +++ b/forui/lib/src/widgets/checkbox.dart @@ -1,6 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'package:forui/src/foundation/stateless_form_field.dart'; import 'package:meta/meta.dart'; @@ -15,7 +16,7 @@ import 'package:forui/forui.dart'; /// See: /// * https://forui.dev/docs/checkbox for working examples. /// * [FCheckboxStyle] for customizing a checkbox's appearance. -class FCheckbox extends StatelessWidget { +class FCheckbox extends FStatelessFormField { /// The style. Defaults to [FThemeData.checkboxStyle]. final FCheckboxStyle? style; @@ -42,44 +43,6 @@ class FCheckbox extends StatelessWidget { /// Called with true if this widget's node gains focus, and false if it loses focus. final ValueChanged? onFocusChange; - /// An optional method to call with the final value when the form is saved via [FormState.save]. - final FormFieldSetter? onSave; - - /// An optional method that validates an input. Returns an error string to display if the input is invalid, or null - /// otherwise. - /// - /// The returned value is exposed by the [FormFieldState.errorText] property. - final FormFieldValidator? validator; - - /// An optional value to initialize the checkbox. Defaults to false. - final bool initialValue; - - /// Whether the form is able to receive user input. - /// - /// Defaults to true. If [autovalidateMode] is not [AutovalidateMode.disabled], the checkbox will be auto validated. - /// Likewise, if this field is false, the widget will not be validated regardless of [autovalidateMode]. - final bool enabled; - - /// Used to enable/disable this checkbox auto validation and update its error text. - /// - /// Defaults to [AutovalidateMode.disabled]. - /// - /// If [AutovalidateMode.onUserInteraction], this checkbox will only auto-validate after its content changes. If - /// [AutovalidateMode.always], it will auto-validate even without user interaction. If [AutovalidateMode.disabled], - /// auto-validation will be disabled. - final AutovalidateMode? autovalidateMode; - - /// Restoration ID to save and restore the state of the checkbox. - /// - /// Setting the restoration ID to a non-null value results in whether or not the checkbox validation persists. - /// - /// The state of this widget is persisted in a [RestorationBucket] claimed from the surrounding [RestorationScope] - /// using the provided restoration ID. - /// - /// See also: - /// * [RestorationManager], which explains how state restoration works in Flutter. - final String? restorationId; - /// Creates a [FCheckbox]. const FCheckbox({ this.style, @@ -90,88 +53,82 @@ class FCheckbox extends StatelessWidget { this.autofocus = false, this.focusNode, this.onFocusChange, - this.onSave, - this.validator, - this.initialValue = false, - this.enabled = true, - this.autovalidateMode, - this.restorationId, + super.onSave, + super.forceErrorText, + super.validator, + super.initialValue = false, + super.enabled = false, + super.autovalidateMode, + super.restorationId, super.key, }); @override - Widget build(BuildContext context) { + Widget builder(BuildContext context, FormFieldState state) { final style = this.style ?? context.theme.checkboxStyle; + final stateStyle = switch ((enabled, state.hasError)) { + (true, false) => style.enabledStyle, + (false, false) => style.disabledStyle, + (_, true) => style.errorStyle, + }; + final value = state.value ?? initialValue; return FocusableActionDetector( + enabled: enabled, autofocus: autofocus, focusNode: focusNode, onFocusChange: onFocusChange, - child: MouseRegion( - cursor: enabled ? SystemMouseCursors.click : MouseCursor.defer, - child: FormField( - onSaved: onSave, - validator: validator, - initialValue: initialValue, - enabled: enabled, - autovalidateMode: autovalidateMode, - restorationId: restorationId, - builder: (state) { - final stateStyle = enabled ? style.enabledStyle : style.disabledStyle; - final value = state.value ?? initialValue; - return Semantics( - label: semanticLabel, - enabled: enabled, - checked: value, - child: GestureDetector( - onTap: enabled - ? () { - state.didChange(!value); - onChange?.call(!value); - } - : null, - child: FLabel( - axis: Axis.horizontal, - state: switch ((enabled, state.hasError)) { - (true, false) => FLabelState.enabled, - (false, false) => FLabelState.disabled, - (_, true) => FLabelState.error, - }, - label: label, - description: description, - error: Text(state.errorText ?? ''), - child: AnimatedSwitcher( - duration: style.animationDuration, - switchInCurve: style.curve, - child: SizedBox.square( - key: ValueKey(value), - dimension: 20, - child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: stateStyle.borderColor, - width: 0.6, - ), - color: value ? stateStyle.checkedBackgroundColor : stateStyle.uncheckedBackgroundColor, - ), - child: value - ? FAssets.icons.check( - height: 15, - width: 15, - colorFilter: ColorFilter.mode( - stateStyle.iconColor, - BlendMode.srcIn, - ), - ) - : const SizedBox(), - ), + mouseCursor: enabled ? SystemMouseCursors.click : MouseCursor.defer, + child: Semantics( + label: semanticLabel, + enabled: enabled, + checked: value, + child: GestureDetector( + onTap: enabled + ? () { + state.didChange(!value); + onChange?.call(!value); + } + : null, + child: FLabel( + axis: Axis.horizontal, + state: switch ((enabled, state.hasError)) { + (true, false) => FLabelState.enabled, + (false, false) => FLabelState.disabled, + (_, true) => FLabelState.error, + }, + label: label, + description: description, + error: Text(state.errorText ?? ''), + child: AnimatedSwitcher( + duration: style.animationDuration, + switchInCurve: style.curve, + child: SizedBox.square( + key: ValueKey(value), + dimension: 16, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: stateStyle.borderColor, + width: 0.6, ), + color: value ? stateStyle.checkedBackgroundColor : stateStyle.uncheckedBackgroundColor, ), + child: value + ? FAssets.icons.check( + height: 15, + width: 15, + colorFilter: ColorFilter.mode( + stateStyle.iconColor, + BlendMode.srcIn, + ), + ) + : const SizedBox(), ), ), - ); - }, + ), + ), ), ), ); @@ -186,13 +143,7 @@ class FCheckbox extends StatelessWidget { ..add(ObjectFlagProperty.has('onChange', onChange)) ..add(DiagnosticsProperty('autofocus', autofocus)) ..add(DiagnosticsProperty('focusNode', focusNode)) - ..add(ObjectFlagProperty.has('onFocusChange', onFocusChange)) - ..add(ObjectFlagProperty.has('onSave', onSave)) - ..add(ObjectFlagProperty.has('validator', validator)) - ..add(DiagnosticsProperty('initialValue', initialValue)) - ..add(DiagnosticsProperty('enabled', enabled)) - ..add(EnumProperty('autovalidateMode', autovalidateMode)) - ..add(StringProperty('restorationId', restorationId)); + ..add(ObjectFlagProperty.has('onFocusChange', onFocusChange)); } } @@ -208,39 +159,50 @@ final class FCheckboxStyle with Diagnosticable { /// Defaults to [Curves.linear]. final Curve curve; + /// The [FLabel]'s style. + final FLabelStyle? labelStyle; + /// The checkbox's style when it's enabled. final FCheckboxStateStyle enabledStyle; /// The checkbox's style when it's disabled. final FCheckboxStateStyle disabledStyle; + /// The checkbox's style when it's in an error state. + final FCheckboxStateStyle errorStyle; + /// Creates a [FCheckboxStyle]. FCheckboxStyle({ required this.enabledStyle, required this.disabledStyle, + required this.errorStyle, this.animationDuration = const Duration(milliseconds: 100), this.curve = Curves.linear, + this.labelStyle, }); /// Creates a [FCheckboxStyle] that inherits its properties from the given [FColorScheme]. FCheckboxStyle.inherit({required FColorScheme colorScheme, required FStyle style}) : animationDuration = const Duration(milliseconds: 100), curve = Curves.linear, + labelStyle = FLabelStyles.inherit(style: style).horizontal, enabledStyle = FCheckboxStateStyle( borderColor: colorScheme.primary, - iconColor: colorScheme.background, + iconColor: colorScheme.primaryForeground, checkedBackgroundColor: colorScheme.primary, uncheckedBackgroundColor: colorScheme.background, - labelTextStyle: style.formFieldStyle.enabledStyle.labelTextStyle, - descriptionTextStyle: style.formFieldStyle.enabledStyle.descriptionTextStyle, ), disabledStyle = FCheckboxStateStyle( borderColor: colorScheme.primary.withOpacity(0.5), - iconColor: colorScheme.background.withOpacity(0.5), + iconColor: colorScheme.primaryForeground.withOpacity(0.5), checkedBackgroundColor: colorScheme.primary.withOpacity(0.5), uncheckedBackgroundColor: colorScheme.background.withOpacity(0.5), - labelTextStyle: style.formFieldStyle.disabledStyle.labelTextStyle, - descriptionTextStyle: style.formFieldStyle.disabledStyle.descriptionTextStyle, + ), + errorStyle = FCheckboxStateStyle( + borderColor: colorScheme.error, + iconColor: colorScheme.errorForeground, + checkedBackgroundColor: colorScheme.error, + uncheckedBackgroundColor: colorScheme.background, ); /// Returns a copy of this [FCheckboxStyle] with the given properties replaced. @@ -263,14 +225,18 @@ final class FCheckboxStyle with Diagnosticable { FCheckboxStyle copyWith({ Duration? animationDuration, Curve? curve, + FLabelStyle? labelStyle, FCheckboxStateStyle? enabledStyle, FCheckboxStateStyle? disabledStyle, + FCheckboxStateStyle? errorStyle, }) => FCheckboxStyle( animationDuration: animationDuration ?? this.animationDuration, curve: curve ?? this.curve, + labelStyle: labelStyle ?? this.labelStyle, enabledStyle: enabledStyle ?? this.enabledStyle, disabledStyle: disabledStyle ?? this.disabledStyle, + errorStyle: errorStyle ?? this.errorStyle, ); @override @@ -279,8 +245,10 @@ final class FCheckboxStyle with Diagnosticable { properties ..add(DiagnosticsProperty('animationDuration', animationDuration)) ..add(DiagnosticsProperty('curve', curve)) + ..add(DiagnosticsProperty('labelStyle', labelStyle)) ..add(DiagnosticsProperty('enabledStyle', enabledStyle)) - ..add(DiagnosticsProperty('disabledStyle', disabledStyle)); + ..add(DiagnosticsProperty('disabledStyle', disabledStyle)) + ..add(DiagnosticsProperty('errorStyle', errorStyle)); } @override @@ -290,11 +258,19 @@ final class FCheckboxStyle with Diagnosticable { runtimeType == other.runtimeType && animationDuration == other.animationDuration && curve == other.curve && + labelStyle == other.labelStyle && enabledStyle == other.enabledStyle && - disabledStyle == other.disabledStyle; + disabledStyle == other.disabledStyle && + errorStyle == other.errorStyle; @override - int get hashCode => animationDuration.hashCode ^ curve.hashCode ^ enabledStyle.hashCode ^ disabledStyle.hashCode; + int get hashCode => + animationDuration.hashCode ^ + curve.hashCode ^ + labelStyle.hashCode ^ + enabledStyle.hashCode ^ + disabledStyle.hashCode ^ + errorStyle.hashCode; } /// A checkbox state's style. @@ -311,20 +287,12 @@ final class FCheckboxStateStyle with Diagnosticable { /// The unchecked checkbox's background color. final Color uncheckedBackgroundColor; - /// The label's text style. - final TextStyle labelTextStyle; - - /// The description's text style. - final TextStyle descriptionTextStyle; - /// Creates a [FCheckboxStateStyle]. const FCheckboxStateStyle({ required this.borderColor, required this.iconColor, required this.checkedBackgroundColor, required this.uncheckedBackgroundColor, - required this.labelTextStyle, - required this.descriptionTextStyle, }); /// Returns a copy of this [FCheckboxStateStyle] with the given properties replaced. @@ -357,8 +325,6 @@ final class FCheckboxStateStyle with Diagnosticable { iconColor: iconColor ?? this.iconColor, checkedBackgroundColor: checkedBackgroundColor ?? this.checkedBackgroundColor, uncheckedBackgroundColor: uncheckedBackgroundColor ?? this.uncheckedBackgroundColor, - labelTextStyle: labelTextStyle ?? this.labelTextStyle, - descriptionTextStyle: descriptionTextStyle ?? this.descriptionTextStyle, ); @override @@ -368,9 +334,7 @@ final class FCheckboxStateStyle with Diagnosticable { ..add(ColorProperty('borderColor', borderColor)) ..add(ColorProperty('checkedIconColor', iconColor)) ..add(ColorProperty('checkedBackgroundColor', checkedBackgroundColor)) - ..add(ColorProperty('uncheckedBackgroundColor', uncheckedBackgroundColor)) - ..add(DiagnosticsProperty('labelTextStyle', labelTextStyle)) - ..add(DiagnosticsProperty('descriptionTextStyle', descriptionTextStyle)); + ..add(ColorProperty('uncheckedBackgroundColor', uncheckedBackgroundColor)); } @override @@ -381,16 +345,12 @@ final class FCheckboxStateStyle with Diagnosticable { borderColor == other.borderColor && iconColor == other.iconColor && checkedBackgroundColor == other.checkedBackgroundColor && - uncheckedBackgroundColor == other.uncheckedBackgroundColor && - labelTextStyle == other.labelTextStyle && - descriptionTextStyle == other.descriptionTextStyle; + uncheckedBackgroundColor == other.uncheckedBackgroundColor; @override int get hashCode => borderColor.hashCode ^ iconColor.hashCode ^ checkedBackgroundColor.hashCode ^ - uncheckedBackgroundColor.hashCode ^ - labelTextStyle.hashCode ^ - descriptionTextStyle.hashCode; + uncheckedBackgroundColor.hashCode; } diff --git a/forui/lib/src/widgets/label.dart b/forui/lib/src/widgets/label.dart index b2a639836..97e9dfad1 100644 --- a/forui/lib/src/widgets/label.dart +++ b/forui/lib/src/widgets/label.dart @@ -266,6 +266,7 @@ class _FVerticalLabel extends StatelessWidget { padding: style.labelPadding, child: DefaultTextStyle( style: stateStyle.labelTextStyle, + textHeightBehavior: const TextHeightBehavior(applyHeightToFirstAscent: false), child: label!, ), ), @@ -278,6 +279,7 @@ class _FVerticalLabel extends StatelessWidget { padding: style.descriptionPadding, child: DefaultTextStyle( style: stateStyle.descriptionTextStyle, + textHeightBehavior: const TextHeightBehavior(applyHeightToFirstAscent: false), child: description!, ), ), @@ -286,6 +288,7 @@ class _FVerticalLabel extends StatelessWidget { padding: style.errorPadding, child: DefaultTextStyle( style: style.formFieldStyle.errorStyle.errorTextStyle, + textHeightBehavior: const TextHeightBehavior(applyHeightToFirstAscent: false), child: error!, ), ), @@ -320,9 +323,16 @@ final class FLabelStyles with Diagnosticable { FLabelStyles.inherit({required FStyle style}) : horizontal = FLabelStyle( formFieldStyle: style.formFieldStyle, - childPadding: const EdgeInsets.only(right: 10), + childPadding: const EdgeInsets.only(right: 8), + descriptionPadding: const EdgeInsets.only(top: 2), + errorPadding: const EdgeInsets.only(top: 2), ), - vertical = FLabelStyle(formFieldStyle: style.formFieldStyle); + vertical = FLabelStyle( + formFieldStyle: style.formFieldStyle, + labelPadding: const EdgeInsets.only(bottom: 5), + descriptionPadding: const EdgeInsets.only(top: 5), + errorPadding: const EdgeInsets.only(top: 5), + ); /// Returns a copy of this [FLabelStyles] with the given properties replaced. /// diff --git a/samples/pubspec.lock b/samples/pubspec.lock index 0c55bb373..c5a1244da 100644 --- a/samples/pubspec.lock +++ b/samples/pubspec.lock @@ -367,18 +367,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -407,18 +407,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mime: dependency: transitive description: @@ -652,10 +652,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" timing: dependency: transitive description: @@ -708,10 +708,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.4" watcher: dependency: transitive description: