Skip to content

Commit 3895c41

Browse files
author
smalla
committed
copy of jorelkimcruz's commit in danvick#96 to fix backspace bug
1 parent bd0ca1a commit 3895c41

File tree

1 file changed

+52
-25
lines changed

1 file changed

+52
-25
lines changed

lib/src/chips_input.dart

+52-25
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ typedef ChipsBuilder<T> = Widget Function(
1515
const kObjectReplacementChar = 0xFFFD;
1616

1717
extension on TextEditingValue {
18-
String get normalCharactersText => String.fromCharCodes(
18+
String get normalCharactersText =>
19+
String.fromCharCodes(
1920
text.codeUnits.where((ch) => ch != kObjectReplacementChar),
2021
);
2122

22-
List<int> get replacementCharacters => text.codeUnits
23-
.where((ch) => ch == kObjectReplacementChar)
24-
.toList(growable: false);
23+
List<int> get replacementCharacters =>
24+
text.codeUnits
25+
.where((ch) => ch == kObjectReplacementChar)
26+
.toList(growable: false);
2527

2628
int get replacementCharactersCount => replacementCharacters.length;
2729
}
@@ -52,7 +54,8 @@ class ChipsInput<T> extends StatefulWidget {
5254
this.allowChipEditing = false,
5355
this.focusNode,
5456
this.initialSuggestions,
55-
}) : assert(maxChips == null || initialValue.length <= maxChips),
57+
})
58+
: assert(maxChips == null || initialValue.length <= maxChips),
5659
super(key: key);
5760

5861
final InputDecoration decoration;
@@ -91,15 +94,16 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
9194
Set<T> _chips = <T>{};
9295
List<T?>? _suggestions;
9396
final StreamController<List<T?>?> _suggestionsStreamController =
94-
StreamController<List<T>?>.broadcast();
97+
StreamController<List<T>?>.broadcast();
9598
int _searchId = 0;
9699
TextEditingValue _value = const TextEditingValue();
97100
TextInputConnection? _textInputConnection;
98101
late SuggestionsBoxController _suggestionsBoxController;
99102
final _layerLink = LayerLink();
100103
final Map<T?, String> _enteredTexts = <T, String>{};
101104

102-
TextInputConfiguration get textInputConfiguration => TextInputConfiguration(
105+
TextInputConfiguration get textInputConfiguration =>
106+
TextInputConfiguration(
103107
inputType: widget.inputType,
104108
obscureText: widget.obscureText,
105109
autocorrect: widget.autocorrect,
@@ -116,6 +120,7 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
116120
widget.maxChips != null && _chips.length >= widget.maxChips!;
117121

118122
FocusNode? _focusNode;
123+
119124
FocusNode get _effectiveFocusNode =>
120125
widget.focusNode ?? (_focusNode ??= FocusNode());
121126
late FocusAttachment _nodeAttachment;
@@ -197,7 +202,7 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
197202
final showTop = topAvailableSpace > bottomAvailableSpace;
198203
// print("showTop: $showTop" );
199204
final compositedTransformFollowerOffset =
200-
showTop ? Offset(0, -size.height) : Offset.zero;
205+
showTop ? Offset(0, -size.height) : Offset.zero;
201206

202207
return StreamBuilder<List<T?>?>(
203208
stream: _suggestionsStreamController.stream,
@@ -217,10 +222,10 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
217222
itemBuilder: (BuildContext context, int index) {
218223
return _suggestions != null
219224
? widget.suggestionBuilder(
220-
context,
221-
this,
222-
_suggestions![index] as T,
223-
)
225+
context,
226+
this,
227+
_suggestions![index] as T,
228+
)
224229
: Container();
225230
},
226231
),
@@ -235,9 +240,9 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
235240
child: !showTop
236241
? suggestionsListView
237242
: FractionalTranslation(
238-
translation: const Offset(0, -1),
239-
child: suggestionsListView,
240-
),
243+
translation: const Offset(0, -1),
244+
child: suggestionsListView,
245+
),
241246
),
242247
);
243248
}
@@ -292,7 +297,10 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
292297
Future.delayed(const Duration(milliseconds: 300), () {
293298
WidgetsBinding.instance.addPostFrameCallback((_) async {
294299
final renderBox = context.findRenderObject() as RenderBox;
295-
await Scrollable.of(context)?.position.ensureVisible(renderBox);
300+
await Scrollable
301+
.of(context)
302+
?.position
303+
.ensureVisible(renderBox);
296304
});
297305
});
298306
}
@@ -301,7 +309,8 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
301309
final localId = ++_searchId;
302310
final results = await widget.findSuggestions(value);
303311
if (_searchId == localId && mounted) {
304-
setState(() => _suggestions =
312+
setState(() =>
313+
_suggestions =
305314
results.where((r) => !_chips.contains(r)).toList(growable: false));
306315
}
307316
_suggestionsStreamController.add(_suggestions ?? []);
@@ -328,7 +337,7 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
328337
oldTextEditingValue.replacementCharactersCount) {
329338
final removedChip = _chips.last;
330339
setState(() =>
331-
_chips = Set.of(_chips.take(value.replacementCharactersCount)));
340+
_chips = Set.of(_chips.take(value.replacementCharactersCount)));
332341
widget.onChanged(_chips.toList(growable: false));
333342
String? putText = '';
334343
if (widget.allowChipEditing && _enteredTexts.containsKey(removedChip)) {
@@ -349,12 +358,13 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
349358
String.fromCharCodes(_chips.map((_) => kObjectReplacementChar)) +
350359
(replaceText ? '' : _value.normalCharactersText) +
351360
putText;
352-
setState(() => _value = _value.copyWith(
353-
text: updatedText,
354-
selection: TextSelection.collapsed(offset: updatedText.length),
355-
//composing: TextRange(start: 0, end: text.length),
356-
composing: TextRange.empty,
357-
));
361+
setState(() =>
362+
_value = _value.copyWith(
363+
text: updatedText,
364+
selection: TextSelection.collapsed(offset: updatedText.length),
365+
//composing: TextRange(start: 0, end: text.length),
366+
composing: TextRange.empty,
367+
));
358368
}
359369
_closeInputConnectionIfNeeded(); //Hack for #34 (https://github.com/danvick/flutter_chips_input/issues/34#issuecomment-684505282). TODO: Find permanent fix
360370
_textInputConnection ??= TextInput.attach(this, textInputConfiguration);
@@ -451,7 +461,23 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
451461
),
452462
);
453463

454-
return NotificationListener<SizeChangedLayoutNotification>(
464+
return RawKeyboardListener(
465+
focusNode: _effectiveFocusNode,
466+
onKey: (event) {
467+
final str = currentTextEditingValue.text;
468+
469+
/// Make sure to filter event since without checking 'RawKeyDownEvent' will trigger this multiple times (2) because of RawKeyUpEvent
470+
if (event.runtimeType.toString() == 'RawKeyDownEvent' &&
471+
event.logicalKey == LogicalKeyboardKey.backspace &&
472+
str.isNotEmpty) {
473+
final sd = str.substring(0, str.length - 1);
474+
475+
/// Make sure to also update cursor position using the TextSelection.collapsed.
476+
updateEditingValue(TextEditingValue(
477+
text: sd,
478+
selection: TextSelection.collapsed(offset: sd.length)));
479+
}
480+
}, child: NotificationListener<SizeChangedLayoutNotification>(
455481
onNotification: (SizeChangedLayoutNotification val) {
456482
WidgetsBinding.instance.addPostFrameCallback((_) async {
457483
_suggestionsBoxController.overlayEntry?.markNeedsBuild();
@@ -485,6 +511,7 @@ class ChipsInputState<T> extends State<ChipsInput<T>>
485511
],
486512
),
487513
),
514+
)
488515
);
489516
}
490517

0 commit comments

Comments
 (0)