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

feat: [DX-1920] Add auto collapse option #635

Merged
merged 6 commits into from
Jun 28, 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
42 changes: 38 additions & 4 deletions optimus/lib/src/form/input_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class OptimusInputField extends StatefulWidget {
this.enableIMEPersonalizedLearning = true,
this.enableSuggestions = true,
this.inline = false,
this.autoCollapse = false,
this.statusBarState,
});

Expand Down Expand Up @@ -82,6 +83,10 @@ class OptimusInputField extends StatefulWidget {
final int? maxCharacters;

/// {@macro flutter.widgets.editableText.minLines}
///
/// If the input is multi-lined we recommend you to set the [keyboardType] to
/// [TextInputType.multiline] otherwise the move to the next line won't work
/// properly.
final int? minLines;
final TextEditingController? controller;
final String? error;
Expand Down Expand Up @@ -161,6 +166,10 @@ class OptimusInputField extends StatefulWidget {
/// the vertical direction.
final bool inline;

/// Controls whether the input should collapse to one line height if not
/// focused.
final bool autoCollapse;

final OptimusStatusBarState? statusBarState;

bool get hasError {
Expand All @@ -178,6 +187,8 @@ class _OptimusInputFieldState extends State<OptimusInputField>
FocusNode? _focusNode;
bool _isShowPasswordEnabled = false;
TextEditingController? _controller;
late int? _minLines = widget.minLines;
late int _maxLines = widget.maxLines;

TextEditingController get _effectiveController =>
widget.controller ?? (_controller ??= TextEditingController());
Expand All @@ -188,19 +199,29 @@ class _OptimusInputFieldState extends State<OptimusInputField>
@override
void initState() {
super.initState();
_effectiveFocusNode.addListener(_handleStateUpdate);
_effectiveFocusNode.addListener(_handleFocusUpdate);
_effectiveController.addListener(_handleStateUpdate);
}

@override
void dispose() {
_effectiveFocusNode.removeListener(_handleStateUpdate);
_effectiveFocusNode.removeListener(_handleFocusUpdate);
_effectiveController.removeListener(_handleStateUpdate);
_focusNode?.dispose();
_controller?.dispose();
super.dispose();
}

@override
void didUpdateWidget(OptimusInputField oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.autoCollapse != oldWidget.autoCollapse ||
widget.minLines != oldWidget.minLines ||
widget.maxLines != oldWidget.maxLines) {
_updateLines();
}
}

bool get _shouldShowInlineError =>
(widget.errorVariant == OptimusInputErrorVariant.inlineTooltip ||
widget.inline) &&
Expand Down Expand Up @@ -228,6 +249,19 @@ class _OptimusInputFieldState extends State<OptimusInputField>
bool get _isPasswordToggleVisible =>
widget.isPasswordField && !widget.showLoader;

bool get _shouldCollapse =>
widget.autoCollapse && !_effectiveFocusNode.hasFocus;

void _handleFocusUpdate() => setState(() {
if (!widget.autoCollapse) return;
_updateLines();
});

void _updateLines() {
_maxLines = _shouldCollapse ? 1 : widget.maxLines;
_minLines = _shouldCollapse ? 1 : widget.minLines;
}

void _handleStateUpdate() => setState(() {});

void _handlePasswordTap() => setState(
Expand Down Expand Up @@ -303,8 +337,8 @@ class _OptimusInputFieldState extends State<OptimusInputField>
autofocus: widget.autofocus,
enableInteractiveSelection: widget.enableInteractiveSelection,
controller: _effectiveController,
maxLines: widget.maxLines,
minLines: widget.minLines,
maxLines: _maxLines,
minLines: _minLines,
onSubmitted: widget.onSubmitted,
textInputAction: widget.textInputAction,
placeholder: widget.placeholder,
Expand Down
23 changes: 22 additions & 1 deletion storybook/lib/stories/input.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:optimus/optimus.dart';
import 'package:storybook/utils.dart';
import 'package:storybook_flutter/storybook_flutter.dart';
Expand Down Expand Up @@ -45,7 +45,13 @@ final Story inputStory = Story(
}
final minLines =
k.sliderInt(label: 'Min lines', initial: 1, min: 1, max: 10);
final keyboardType = k.options(
label: 'Keyboard Type:',
initial: null,
options: KeyboardType.values.toOptions().withEmpty(),
);
final inline = k.boolean(label: 'Inline', initial: false);
final autoCollapse = k.boolean(label: 'Auto Collapse', initial: true);

final statusBar = k.options(
label: 'Status Bar',
Expand All @@ -64,6 +70,7 @@ final Story inputStory = Story(
maxCharacters: maxChars,
minLines: minLines,
maxLines: minLines,
keyboardType: keyboardType?.inputType,
prefix: prefix.isNotEmpty ? Text(prefix) : null,
suffix: suffix.isNotEmpty ? Text(suffix) : null,
leading: leadingIcon == null ? null : Icon(leadingIcon),
Expand All @@ -78,6 +85,7 @@ final Story inputStory = Story(
options: sizeOptions,
),
inline: inline,
autoCollapse: autoCollapse,
label: k.text(label: 'Label', initial: 'Optimus input field'),
placeholder:
k.text(label: 'Placeholder', initial: 'Put some hint here...'),
Expand All @@ -90,3 +98,16 @@ final Story inputStory = Story(
);
},
);

enum KeyboardType {
text(TextInputType.text),
multiline(TextInputType.multiline),
number(TextInputType.number),
phone(TextInputType.phone),
datetime(TextInputType.datetime),
emailAddress(TextInputType.emailAddress),
url(TextInputType.url);

const KeyboardType(this.inputType);
final TextInputType inputType;
}
Loading