Skip to content

Commit

Permalink
fix(UX-1105): Updated search bar to use internal text field (#186)
Browse files Browse the repository at this point in the history
fix: Components using the internal text input are now sized correctly
  • Loading branch information
mikecoomber authored Oct 14, 2024
1 parent ef050af commit 38b865b
Show file tree
Hide file tree
Showing 14 changed files with 92 additions and 127 deletions.
1 change: 1 addition & 0 deletions example/lib/pages/components/search_bar_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class _SearchBarExampleState extends State<SearchBarExample> {
padding: const EdgeInsets.all(20),
child: ZetaSearchBar(
onChange: (value) {},
showSpeechToText: false,
textInputAction: TextInputAction.search,
onFieldSubmitted: (text) {
print(text);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Widget searchBarUseCase(BuildContext context) {
size: size,
shape: shape,
disabled: disabled,
hintText: hint,
placeholder: hint,
showSpeechToText: showSpeechToText,
onChange: (value) {
if (value == null) return;
Expand Down
1 change: 1 addition & 0 deletions lib/src/components/date_input/date_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class ZetaDateInput extends ZetaFormField<DateTime> {

return InternalTextInput(
label: label,
constrained: true,
hintText: hintText,
errorText: field.errorText ?? errorText,
size: size,
Expand Down
1 change: 1 addition & 0 deletions lib/src/components/password/password_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class ZetaPasswordInput extends ZetaTextFormField {
requirementLevel: requirementLevel,
errorText: field.errorText ?? errorText,
onSubmit: onSubmit,
constrained: true,
disabled: disabled,
obscureText: state._obscureText,
semanticLabel: semanticLabel,
Expand Down
167 changes: 56 additions & 111 deletions lib/src/components/search_bar/search_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import '../../../zeta_flutter.dart';
import '../../interfaces/form_field.dart';
import '../buttons/input_icon_button.dart';
import '../text_input/internal_text_input.dart';

/// ZetaSearchBar provides input field for searching.
/// {@category Components}
Expand All @@ -25,8 +26,9 @@ class ZetaSearchBar extends ZetaTextFormField {
super.initialValue,
this.size = ZetaWidgetSize.medium,
this.shape = ZetaWidgetBorder.rounded,
@Deprecated('Use hintText instead. ' 'deprecated as of 0.15.0') String? hint,
this.hintText,
@Deprecated('hint has been removed. ' 'deprecated as of 0.15.0') String? hint,
@Deprecated('Use placeholder instead. ' 'deprecated as of 0.16.0') String? hintText,
this.placeholder,
this.onSpeechToText,
this.showSpeechToText = true,
@Deprecated('Use disabled instead. ' 'enabled is deprecated as of 0.11.0') bool enabled = true,
Expand All @@ -49,130 +51,74 @@ class ZetaSearchBar extends ZetaTextFormField {
_ => zeta.radius.none,
};

final defaultInputBorder = OutlineInputBorder(
borderRadius: borderRadius,
borderSide: BorderSide(color: zeta.colors.cool.shade40),
);

final focusedBorder = defaultInputBorder.copyWith(
borderSide: BorderSide(
color: zeta.colors.blue.shade50,
width: zeta.spacing.minimum,
),
);

final disabledborder = defaultInputBorder.copyWith(
borderSide: BorderSide(color: zeta.colors.borderDisabled),
);

late final double iconSize;
late final double padding;

switch (size) {
case ZetaWidgetSize.large:
iconSize = zeta.spacing.xl_2;
padding = zeta.spacing.medium;
case ZetaWidgetSize.medium:
iconSize = zeta.spacing.xl;
padding = zeta.spacing.small;
case ZetaWidgetSize.small:
iconSize = zeta.spacing.large;
padding = zeta.spacing.minimum;
}

return ZetaRoundedScope(
rounded: shape != ZetaWidgetBorder.sharp,
child: Semantics(
excludeSemantics: disabled,
label: disabled ? hintText ?? 'Search' : null, // TODO(UX-1003): Localize
label: disabled ? placeholder ?? 'Search' : null, // TODO(UX-1003): Localize
enabled: disabled ? false : null,
child: TextFormField(
child: InternalTextInput(
focusNode: focusNode,
enabled: !disabled,
size: size,
disabled: disabled,
constrained: true,
borderRadius: borderRadius,
controller: state.effectiveController,
keyboardType: TextInputType.text,
textInputAction: textInputAction,
onFieldSubmitted: onFieldSubmitted,
onChanged: state.onChange,
style: ZetaTextStyles.bodyMedium,
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: padding,
),
hintText: hintText ?? 'Search', // TODO(UX-1003): Localize
hintStyle: ZetaTextStyles.bodyMedium.copyWith(
color: !disabled ? zeta.colors.textSubtle : zeta.colors.textDisabled,
),
prefixIcon: Padding(
padding: EdgeInsets.only(left: zeta.spacing.medium, right: zeta.spacing.small),
child: ZetaIcon(
ZetaIcons.search,
color: !disabled ? zeta.colors.cool.shade70 : zeta.colors.cool.shade50,
size: iconSize,
),
),
prefixIconConstraints: BoxConstraints(
minHeight: zeta.spacing.xl_2,
minWidth: zeta.spacing.xl_2,
),
suffixIcon: IntrinsicHeight(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (state.effectiveController.text.isNotEmpty && !disabled) ...[
Semantics(
container: true,
button: true,
excludeSemantics: true,
label: clearSemanticLabel,
child: InputIconButton(
icon: ZetaIcons.cancel,
onTap: () => state.onChange(''),
disabled: disabled,
size: size,
color: zeta.colors.iconSubtle,
key: const ValueKey('search-clear-btn'),
),
),
if (showSpeechToText)
SizedBox(
height: iconSize,
child: VerticalDivider(
color: zeta.colors.cool.shade40,
width: 5,
thickness: 1,
),
),
],
if (showSpeechToText)
Semantics(
label: microphoneSemanticLabel,
container: true,
button: true,
excludeSemantics: true,
child: InputIconButton(
icon: ZetaIcons.microphone,
onTap: state.onSpeechToText,
key: const ValueKey('speech-to-text-btn'),
disabled: disabled,
size: size,
color: zeta.colors.iconDefault,
),
placeholder: placeholder ?? 'Search', // TODO(UX-1003): Localize
onSubmit: onFieldSubmitted,
onChange: state.onChange,
prefix: ZetaIcon(
ZetaIcons.search,
color: !disabled ? zeta.colors.iconSubtle : zeta.colors.iconDisabled,
size: iconSize,
),
suffix: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (state.effectiveController.text.isNotEmpty && !disabled) ...[
InputIconButton(
icon: ZetaIcons.cancel,
onTap: () => state.onChange(''),
disabled: disabled,
size: size,
semanticLabel: clearSemanticLabel,
color: zeta.colors.iconSubtle,
key: const ValueKey('search-clear-btn'),
),
if (showSpeechToText)
SizedBox(
height: iconSize,
child: VerticalDivider(
color: zeta.colors.cool.shade40,
width: 5,
thickness: 1,
),
],
),
),
suffixIconConstraints: BoxConstraints(
minHeight: zeta.spacing.xl_2,
minWidth: zeta.spacing.xl_2,
),
filled: !disabled ? null : true,
fillColor: !disabled ? null : zeta.colors.cool.shade30,
enabledBorder: defaultInputBorder,
focusedBorder: focusedBorder,
disabledBorder: disabledborder,
),
],
if (showSpeechToText)
InputIconButton(
icon: ZetaIcons.microphone,
onTap: state.onSpeechToText,
key: const ValueKey('speech-to-text-btn'),
disabled: disabled,
semanticLabel: microphoneSemanticLabel,
size: size,
color: zeta.colors.iconDefault,
),
],
),
),
),
Expand All @@ -184,14 +130,13 @@ class ZetaSearchBar extends ZetaTextFormField {
/// Default is [ZetaWidgetSize.medium]
final ZetaWidgetSize size;

/// Placeholder text for the search field.
final String? placeholder;

/// Determines the shape of the input field.
/// Default is [ZetaWidgetBorder.rounded]
final ZetaWidgetBorder shape;

/// If provided, displays a hint inside the input field.
/// Default is `Search`.
final String? hintText;

/// The type of action button to use for the keyboard.
final TextInputAction? textInputAction;

Expand Down Expand Up @@ -224,14 +169,14 @@ class ZetaSearchBar extends ZetaTextFormField {
properties
..add(EnumProperty<ZetaWidgetSize>('size', size))
..add(EnumProperty<ZetaWidgetBorder>('shape', shape))
..add(StringProperty('hintText', hintText))
..add(StringProperty('initialValue', initialValue))
..add(ObjectFlagProperty<VoidCallback?>.has('onSpeechToText', onSpeechToText))
..add(DiagnosticsProperty<bool>('showSpeechToText', showSpeechToText))
..add(DiagnosticsProperty<FocusNode>('focusNode', focusNode))
..add(EnumProperty<TextInputAction>('textInputAction', textInputAction))
..add(StringProperty('microphoneSemanticLabel', microphoneSemanticLabel))
..add(StringProperty('clearSemanticLabel', clearSemanticLabel));
..add(StringProperty('clearSemanticLabel', clearSemanticLabel))
..add(StringProperty('placeholder', placeholder));
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/src/components/select_input/select_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class ZetaSelectInput<T> extends ZetaFormField<T> {
builder: (context, _, controller) {
return InternalTextInput(
size: size,
constrained: true,
requirementLevel: requirementLevel,
disabled: disabled,
controller: state.inputController,
Expand Down
33 changes: 24 additions & 9 deletions lib/src/components/text_input/internal_text_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class InternalTextInput extends ZetaStatefulWidget {
this.externalPrefix,
this.semanticLabel,
this.borderRadius,
this.textInputAction,
this.constrained = false,
}) : requirementLevel = requirementLevel ?? ZetaFormFieldRequirement.none,
assert(prefix == null || prefixText == null, 'Only one of prefix or prefixText can be accepted.'),
assert(suffix == null || suffixText == null, 'Only one of suffix or suffixText can be accepted.');
Expand Down Expand Up @@ -130,6 +132,12 @@ class InternalTextInput extends ZetaStatefulWidget {
/// The widget displayed before the input.
final Widget? externalPrefix;

/// The action to take when the user submits the input.
final TextInputAction? textInputAction;

/// Determines if the prefix and suffix should be constrained.
final bool constrained;

@override
State<InternalTextInput> createState() => InternalTextInputState();
@override
Expand All @@ -156,7 +164,9 @@ class InternalTextInput extends ZetaStatefulWidget {
..add(DiagnosticsProperty<TextInputType?>('keyboardType', keyboardType))
..add(DiagnosticsProperty<FocusNode?>('focusNode', focusNode))
..add(DiagnosticsProperty<BorderRadius?>('borderRadius', borderRadius))
..add(StringProperty('semanticLabel', semanticLabel));
..add(StringProperty('semanticLabel', semanticLabel))
..add(EnumProperty<TextInputAction?>('textInputAction', textInputAction))
..add(DiagnosticsProperty<bool>('constrained', constrained));
}
}

Expand Down Expand Up @@ -197,7 +207,7 @@ class InternalTextInputState extends State<InternalTextInput> {
case ZetaWidgetSize.medium:
return EdgeInsets.symmetric(
horizontal: Zeta.of(context).spacing.medium,
vertical: Zeta.of(context).spacing.small,
vertical: Zeta.of(context).spacing.medium,
);
}
}
Expand All @@ -221,7 +231,7 @@ class InternalTextInputState extends State<InternalTextInput> {
width = Zeta.of(context).spacing.xl_6;
height = Zeta.of(context).spacing.xl_6;
case ZetaWidgetSize.small:
width = Zeta.of(context).spacing.xl_6;
width = Zeta.of(context).spacing.xl_4;
height = Zeta.of(context).spacing.xl_4;
}
return BoxConstraints(
Expand Down Expand Up @@ -277,11 +287,11 @@ class InternalTextInputState extends State<InternalTextInput> {
);

OutlineInputBorder _focusedBorder(bool rounded) => _baseBorder(rounded).copyWith(
borderSide: BorderSide(color: _colors.primary.shade50, width: ZetaBorders.medium),
); // TODO(mikecoomber): change to colors.borderPrimary when added
borderSide: BorderSide(color: _colors.borderPrimary, width: ZetaBorders.medium),
);

OutlineInputBorder _errorBorder(bool rounded) => _baseBorder(rounded).copyWith(
borderSide: BorderSide(color: _colors.error, width: ZetaBorders.medium),
borderSide: BorderSide(color: _colors.borderNegative, width: ZetaBorders.medium),
);

@override
Expand Down Expand Up @@ -337,6 +347,7 @@ class InternalTextInputState extends State<InternalTextInput> {
onChanged: widget.onChange,
onSubmitted: widget.onSubmit,
style: _baseTextStyle,
textInputAction: widget.textInputAction,
cursorErrorColor: _colors.error,
obscureText: widget.obscureText,
focusNode: widget.focusNode,
Expand All @@ -345,9 +356,11 @@ class InternalTextInputState extends State<InternalTextInput> {
contentPadding: _contentPadding,
filled: true,
prefixIcon: _prefix,
prefixIconConstraints: widget.prefixText != null ? _affixConstraints : null,
prefixIconConstraints:
widget.prefixText != null || widget.constrained ? _affixConstraints : null,
suffixIcon: _suffix,
suffixIconConstraints: widget.suffixText != null ? _affixConstraints : null,
suffixIconConstraints:
widget.suffixText != null || widget.constrained ? _affixConstraints : null,
focusColor: _backgroundColor,
hoverColor: _backgroundColor,
fillColor: _backgroundColor,
Expand All @@ -358,7 +371,9 @@ class InternalTextInputState extends State<InternalTextInput> {
errorBorder: widget.disabled ? _baseBorder(rounded) : _errorBorder(rounded),
hintText: widget.placeholder,
errorText: widget.errorText,
hintStyle: _baseTextStyle,
hintStyle: _baseTextStyle.copyWith(
color: widget.disabled ? _colors.textDisabled : _colors.textSubtle,
),
errorStyle: const TextStyle(height: 0.001, color: Colors.transparent),
),
),
Expand Down
1 change: 1 addition & 0 deletions lib/src/components/time_input/time_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class ZetaTimeInput extends ZetaFormField<TimeOfDay> {
return InternalTextInput(
label: label,
hintText: hintText,
constrained: true,
errorText: field.errorText ?? errorText,
size: size,
placeholder: state.timeFormat,
Expand Down
Binary file modified test/src/components/search_bar/golden/search_bar_default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/src/components/search_bar/golden/search_bar_full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/src/components/search_bar/golden/search_bar_medium.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/src/components/search_bar/golden/search_bar_sharp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/src/components/search_bar/golden/search_bar_small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 38b865b

Please sign in to comment.