Skip to content
Open
Changes from 3 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
12 changes: 6 additions & 6 deletions examples/travel_app/lib/src/catalog/text_input_chip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,25 @@ class _TextInputChip extends StatefulWidget {
}

class _TextInputChipState extends State<_TextInputChip> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The changes look good and correctly implement the desired functionality. However, I noticed that tests for this new behavior are missing. The repository's style guide states that code should be tested.1

Please consider adding a widget test for _TextInputChip to verify:

  • The chip displays the label when no value is provided.
  • The chip displays the initialValue when provided.
  • The chip displays the user-entered text after submission.
  • The chip reverts to displaying the label when the user clears the text.

Style Guide References

Footnotes

  1. Code should be tested. (link)

late String _currentValue;
final TextEditingController _textController = TextEditingController();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The TextEditingController _textController is initialized here but never disposed of, which will cause a memory leak. You should override the dispose method in _TextInputChipState and call _textController.dispose() inside it.

For example:

@override
void dispose() {
  _textController.dispose();
  super.dispose();
}


@override
void initState() {
super.initState();
_currentValue = widget.initialValue ?? widget.label;
_textController.text = widget.initialValue ?? '';
}
Comment on lines 81 to 92

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The initialValue is not being handled correctly. Currently, it's set on _textController in initState, but this value is overwritten in onSelected before the text field is displayed. Furthermore, this doesn't make the initialValue appear on the chip label itself, as the build method only reads from widget.values.

To ensure initialValue is properly used as the default value, it should be used to populate widget.values when the widget is first initialized and no other value is present.

Suggested change
void initState() {
super.initState();
_currentValue = widget.initialValue ?? widget.label;
_textController.text = widget.initialValue ?? '';
}
void initState() {
super.initState();
if (widget.values[widget.widgetId] == null && widget.initialValue != null) {
widget.values[widget.widgetId] = widget.initialValue;
}
}


@override
Widget build(BuildContext context) {
final value = widget.values[widget.widgetId];
final currentValue = value is String ? value : null;
final text = currentValue ?? widget.label;
return FilterChip(
label: Text(_currentValue),
label: Text(text),
selected: false,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
onSelected: (bool selected) {
_textController.text = currentValue ?? '';
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
Expand All @@ -110,9 +112,7 @@ class _TextInputChipState extends State<_TextInputChip> {
final newValue = _textController.text;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The new value from the text controller should be trimmed before checking if it's empty. This handles cases where a user might input only whitespace, which would currently result in an empty-looking chip instead of it reverting to the label text.

Suggested change
final newValue = _textController.text;
final newValue = _textController.text.trim();

if (newValue.isNotEmpty) {
widget.values[widget.widgetId] = newValue;
setState(() {
_currentValue = newValue;
});
setState(() {});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Calling setState here is necessary because widget.values was mutated directly on the previous line. While this works for this widget, directly mutating a widget property like values is an anti-pattern in Flutter and goes against common framework principles.1 It can lead to inconsistent UI state if other widgets also depend on this values map but are not rebuilt.

A better approach is to use a callback to notify the parent widget of the change, and let the parent update the state. This is known as 'lifting state up' and leads to more predictable state management.

Style Guide References

Footnotes

Navigator.pop(context);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current logic prevents the user from clearing the input. If the user erases the text and presses 'Done', newValue.isNotEmpty is false, and the modal sheet doesn't close, which is a poor user experience.

To fix this, you should handle the case where the new value is empty by removing the corresponding entry from widget.values. The modal should always be closed when 'Done' is pressed.

                      if (newValue.isNotEmpty) {
                        widget.values[widget.widgetId] = newValue;
                      } else {
                        widget.values.remove(widget.widgetId);
                      }
                      setState(() {});
                      Navigator.pop(context);

},
Expand Down
Loading