Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(UX-1105): Updated search bar to use internal text field #186

Merged
merged 2 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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