Skip to content

Commit

Permalink
feat: [DX-1920] Add auto collapse option (#635)
Browse files Browse the repository at this point in the history
  • Loading branch information
witwash authored Jun 28, 2024
1 parent 1250c25 commit 28e70ca
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 5 deletions.
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;
}

0 comments on commit 28e70ca

Please sign in to comment.