Skip to content

Commit

Permalink
Display error message under description
Browse files Browse the repository at this point in the history
  • Loading branch information
Pante committed Aug 7, 2024
1 parent 2b9df77 commit 5c0f2f5
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 32 deletions.
22 changes: 14 additions & 8 deletions docs/pages/docs/text-field.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -168,16 +168,22 @@ FTextField.multiline(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FTextField.email(
hint: '[email protected]',
help: const Text(''),
validator: (value) => (value?.contains('@') ?? false) ? null : 'Please enter a valid email.',
SizedBox(
height: 110,
child: FTextField.email(
hint: '[email protected]',
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) => (value?.contains('@') ?? false) ? null : 'Please enter a valid email.',
),
),
const SizedBox(height: 4),
FTextField.password(
hint: '',
help: const Text(''),
validator: (value) => 8 <= (value?.length ?? 0) ? null : 'Password must be at least 8 characters long.',
SizedBox(
height: 110,
child: FTextField.password(
hint: '',
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) => 8 <= (value?.length ?? 0) ? null : 'Password must be at least 8 characters long.',
),
),
const SizedBox(height: 30),
FButton(
Expand Down
6 changes: 6 additions & 0 deletions forui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@

* Change `FTextFieldStyle` to inherit from `FFormFieldStyle`.

* Change `FTextField` to display error under description instead of replacing it.

* **Breaking:** Remove `FTextField.error`.

* **Breaking:** Change `FTextField.help` to `FTextField.description`.

* **Breaking:** Change how `FTextFieldStyle` stores various state-dependent styles.

### Fixes

* Fix `FTextField` not changing error text color when an error occurs.
Expand Down
6 changes: 2 additions & 4 deletions forui/lib/src/widgets/text_field/text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ part 'text_form_field.dart';
/// See:
/// * https://forui.dev/docs/text-field for working examples.
/// * [FTextFieldStyle] for customizing a text field's appearance.
/// * [_Field] for a text field that integrates with a [Form].
/// * [TextField] for more details about working with a text field.
final class FTextField extends StatelessWidget {
static Widget _contextMenuBuilder(
Expand Down Expand Up @@ -457,10 +456,9 @@ final class FTextField extends StatelessWidget {
/// The returned value is exposed by the [FormFieldState.errorText] property. It transforms the text using
/// [errorBuilder].
///
/// Alternating between error and normal state can cause the height of the [_Field] to change if no other
/// Alternating between error and normal state can cause the height of the [FTextField] to change if no other
/// subtext decoration is set on the field. To create a field whose height is fixed regardless of whether or not an
/// error is displayed, either wrap the [_Field] in a fixed height parent like [SizedBox], or set the [description]
/// parameter to a space.
/// error is displayed, wrap the [FTextField] in a fixed height parent like [SizedBox].
final FormFieldValidator<String>? validator;

/// An optional value to initialize the form field to, or null otherwise.
Expand Down
19 changes: 16 additions & 3 deletions forui/lib/src/widgets/text_field/text_field_state_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ final class FTextFieldErrorStyle extends FTextFieldStateStyle {
/// The error's [TextStyle].
final TextStyle errorTextStyle;

/// The duration of the error fade-in animation.
///
/// Defaults to 100 milliseconds.
final Duration animationDuration;

/// Creates a [FTextFieldErrorStyle].
FTextFieldErrorStyle({
required this.errorTextStyle,
Expand All @@ -158,6 +163,7 @@ final class FTextFieldErrorStyle extends FTextFieldStateStyle {
required super.descriptionTextStyle,
required super.focusedStyle,
required super.unfocusedStyle,
this.animationDuration = const Duration(milliseconds: 100),
});

/// Creates a [FTextFieldErrorStyle] that inherits its properties.
Expand All @@ -170,6 +176,7 @@ final class FTextFieldErrorStyle extends FTextFieldStateStyle {
required super.typography,
required super.style,
}) : errorTextStyle = formFieldErrorStyle.errorTextStyle,
animationDuration = const Duration(milliseconds: 100),
super.inherit(formFieldStateStyle: formFieldErrorStyle);

/// Returns a copy of this [FTextFieldStateStyle] with the given properties replaced.
Expand Down Expand Up @@ -197,6 +204,7 @@ final class FTextFieldErrorStyle extends FTextFieldStateStyle {
TextStyle? descriptionTextStyle,
FTextFieldBorderStyle? focusedStyle,
FTextFieldBorderStyle? unfocusedStyle,
Duration? animationDuration,
}) =>
FTextFieldErrorStyle(
errorTextStyle: errorTextStyle ?? this.errorTextStyle,
Expand All @@ -206,12 +214,15 @@ final class FTextFieldErrorStyle extends FTextFieldStateStyle {
descriptionTextStyle: descriptionTextStyle ?? this.descriptionTextStyle,
focusedStyle: focusedStyle ?? this.focusedStyle,
unfocusedStyle: unfocusedStyle ?? this.unfocusedStyle,
animationDuration: animationDuration ?? this.animationDuration,
);

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty('errorTextStyle', errorTextStyle));
properties
..add(DiagnosticsProperty('errorTextStyle', errorTextStyle))
..add(DiagnosticsProperty('animationDuration', animationDuration));
}

@override
Expand All @@ -225,7 +236,8 @@ final class FTextFieldErrorStyle extends FTextFieldStateStyle {
descriptionTextStyle == other.descriptionTextStyle &&
focusedStyle == other.focusedStyle &&
unfocusedStyle == other.unfocusedStyle &&
errorTextStyle == other.errorTextStyle;
errorTextStyle == other.errorTextStyle &&
animationDuration == other.animationDuration;

@override
int get hashCode =>
Expand All @@ -235,7 +247,8 @@ final class FTextFieldErrorStyle extends FTextFieldStateStyle {
descriptionTextStyle.hashCode ^
focusedStyle.hashCode ^
unfocusedStyle.hashCode ^
errorTextStyle.hashCode;
errorTextStyle.hashCode ^
animationDuration.hashCode;
}

/// A [FTextField] border's style.
Expand Down
2 changes: 2 additions & 0 deletions forui/lib/src/widgets/text_field/text_field_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ final class FTextFieldStyle with Diagnosticable {
/// after the scroll.
final EdgeInsets scrollPadding;



/// The style when this text field is enabled.
final FTextFieldNormalStyle enabledStyle;

Expand Down
23 changes: 14 additions & 9 deletions forui/lib/src/widgets/text_field/text_form_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ class _Field extends FormField<String> {
FTextField parent,
FTextFieldStyle style,
FTextFieldStateStyle stateStyle,
) =>
InputDecoration(
) => InputDecoration(
suffixIcon: parent.suffix,
// 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)),
Expand All @@ -18,13 +17,6 @@ class _Field extends FormField<String> {
? null
: DefaultTextStyle.merge(style: stateStyle.descriptionTextStyle, child: parent.description!),
helperStyle: stateStyle.descriptionTextStyle,
error: state.errorText == null
? null
: DefaultTextStyle.merge(
style: stateStyle.descriptionTextStyle,
child: parent.errorBuilder(state.context, state.errorText!),
),
errorStyle: stateStyle.descriptionTextStyle,
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: style.disabledStyle.unfocusedStyle.color,
Expand Down Expand Up @@ -148,6 +140,19 @@ class _Field extends FormField<String> {
spellCheckConfiguration: parent.spellCheckConfiguration,
magnifierConfiguration: parent.magnifierConfiguration,
),
AnimatedSwitcher(
duration: style.errorStyle.animationDuration,
child: switch (state.errorText) {
null => const SizedBox(),
final error => Padding(
padding: const EdgeInsets.only(top: 7, bottom: 4),
child: DefaultTextStyle.merge(
style: style.errorStyle.errorTextStyle,
child: Text(error),
),
),
},
),
],
),
);
Expand Down
22 changes: 14 additions & 8 deletions samples/lib/widgets/text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,22 @@ class _LoginFormState extends State<LoginForm> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FTextField.email(
hint: '[email protected]',
description: const Text(''),
validator: (value) => (value?.contains('@') ?? false) ? null : 'Please enter a valid email.',
SizedBox(
height: 110,
child: FTextField.email(
hint: '[email protected]',
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) => (value?.contains('@') ?? false) ? null : 'Please enter a valid email.',
),
),
const SizedBox(height: 4),
FTextField.password(
hint: '',
description: const Text(''),
validator: (value) => 8 <= (value?.length ?? 0) ? null : 'Password must be at least 8 characters long.',
SizedBox(
height: 110,
child: FTextField.password(
hint: '',
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) => 8 <= (value?.length ?? 0) ? null : 'Password must be at least 8 characters long.',
),
),
const SizedBox(height: 30),
FButton(
Expand Down

0 comments on commit 5c0f2f5

Please sign in to comment.