Open anthonycic opened 5 years ago
YES! WE NEED THIS!
For everyone who's asking the same thing, i find another solution :
SimpleAutocompleteFormField
Check on Google.
Here my quick and dirty, only-for-my-needs snippet. No time now to make it more configurable and push a PR
listSuggestionsEntry = OverlayEntry(builder: (context) {
return Positioned(
width: width,
child: CompositedTransformFollower(
link: _layerLink,
showWhenUnlinked: false,
offset: Offset(0.0, height),
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: width,
maxWidth: width,
minHeight: 0,
maxHeight: max(0,
MediaQuery.of(context).viewInsets.bottom - height - 15)),
child: Card(
child: SingleChildScrollView(
child: Column(
children: filteredSuggestions.map((suggestion) {
return Row(children: [
Expanded(
child: InkWell(
child: itemBuilder(context, suggestion),
onTap: () {
setState(() {
if (submitOnSuggestionTap) {
String Text = suggestion.toString();
textField.controller.text = Text;
textField.focusNode.unfocus();
itemSubmitted(suggestion);
if (clearOnSubmit) {
clear();
}
} else {
String Text = suggestion.toString();
textField.controller.text = Text;
textChanged(Text);
}
});
}))
]);
}).toList(),
))))));
}
Hope it helps
@tuarrep Tried to use this snippet but it doesn't even pass syntax check. Flutter reports "Too many positional arguments 0 expected but 1 found". It seems the code snippet is incomplete to replace the whole listSuggestionsEntry assignment. Also when trying to collapse in Android Studio it has at least missing brackets. Tried adding those but it didn't help on the positional argument error.
Can you please post a complete example block for filling listSuggestionsEntry
Update: I found the missing part but now I wonder why only 2 entries are shown and why there's now scrollbar
@marcelser
It works fine on my side. This snippet was extracted from a working and published app (I've added missing bracket).
Without more precise information I can't help you.
I tried this snippet, turn out the overlay does not show up.
I'm sorry @sirapolw I couldn't help you. It was working for me in December. Since then the project was shipped to my client and I do not maintain it anymore.
`import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'dart:math';
typedef Widget AutoCompleteOverlayItemBuilder
typedef bool Filter
typedef InputEventCallback
typedef StringCallback(String data);
class AutoCompleteTextField
final InputDecoration decoration; final TextStyle style; final TextInputType keyboardType; final TextInputAction textInputAction; final TextCapitalization textCapitalization; final TextEditingController controller; final FocusNode focusNode;
AutoCompleteTextField(
{@required
this.itemSubmitted, //Callback on item selected, this is the item selected of type
void clear() => key.currentState.clear();
void addSuggestion(T suggestion) => key.currentState.addSuggestion(suggestion);
void removeSuggestion(T suggestion) => key.currentState.removeSuggestion(suggestion);
void updateSuggestions(List
void triggerSubmitted() => key.currentState.triggerSubmitted();
void updateDecoration(
{InputDecoration decoration,
List
TextField get textField => key.currentState.textField;
@override
State
class AutoCompleteTextFieldState
TextField textField;
List
String currentText = "";
InputDecoration decoration;
List
AutoCompleteTextFieldState( this.suggestions, this.textChanged, this.textSubmitted, this.onFocusChanged, this.itemSubmitted, this.itemBuilder, this.itemSorter, this.itemFilter, this.submitOnSuggestionTap, this.clearOnSubmit, this.minLength, this.inputFormatters, this.textCapitalization, this.decoration, this.style, this.keyboardType, this.textInputAction, this.controller, this.focusNode) { textField = new TextField( inputFormatters: inputFormatters, textCapitalization: textCapitalization, decoration: decoration, style: style, keyboardType: keyboardType, focusNode: focusNode ?? new FocusNode(), controller: controller ?? new TextEditingController(), textInputAction: textInputAction, onChanged: (newText) { currentText = newText; updateOverlay(newText);
if (textChanged != null) {
textChanged(newText);
}
},
onTap: () {
updateOverlay(currentText);
},
onSubmitted: (submittedText) =>
triggerSubmitted(submittedText: submittedText),
);
if (this.controller != null && this.controller.text != null) {
currentText = this.controller.text;
}
textField.focusNode.addListener(() {
if (onFocusChanged != null) {
onFocusChanged(textField.focusNode.hasFocus);
}
if (!textField.focusNode.hasFocus) {
filteredSuggestions = [];
updateOverlay();
} else if (!(currentText == "" || currentText == null)) {
updateOverlay(currentText);
}
});
}
void updateDecoration(
InputDecoration decoration,
List
if (inputFormatters != null) {
this.inputFormatters = inputFormatters;
}
if (textCapitalization != null) {
this.textCapitalization = textCapitalization;
}
if (style != null) {
this.style = style;
}
if (keyboardType != null) {
this.keyboardType = keyboardType;
}
if (textInputAction != null) {
this.textInputAction = textInputAction;
}
setState(() {
textField = new TextField(
inputFormatters: this.inputFormatters,
textCapitalization: this.textCapitalization,
decoration: this.decoration,
style: this.style,
keyboardType: this.keyboardType,
focusNode: focusNode ?? new FocusNode(),
controller: controller ?? new TextEditingController(),
textInputAction: this.textInputAction,
onChanged: (newText) {
currentText = newText;
updateOverlay(newText);
if (textChanged != null) {
textChanged(newText);
}
},
onTap: () {
updateOverlay(currentText);
},
onSubmitted: (submittedText) =>
triggerSubmitted(submittedText: submittedText),
);
});
}
void triggerSubmitted({submittedText}) { submittedText == null ? textSubmitted(currentText) : textSubmitted(submittedText);
if (clearOnSubmit) {
clear();
}
}
void clear() { textField.controller.clear(); currentText = ""; updateOverlay(); }
void addSuggestion(T suggestion) { suggestions.add(suggestion); updateOverlay(currentText); }
void removeSuggestion(T suggestion) { suggestions.contains(suggestion) ? suggestions.remove(suggestion) : throw "List does not contain suggestion and therefore cannot be removed"; updateOverlay(currentText); }
void updateSuggestions(List
void updateOverlay([String query]) { if (listSuggestionsEntry == null) { final Size textFieldSize = (context.findRenderObject() as RenderBox).size; final width = textFieldSize.width; final height = textFieldSize.height; listSuggestionsEntry = new OverlayEntry(builder: (context) { return new Positioned( width: width, child: CompositedTransformFollower( link: _layerLink, showWhenUnlinked: false, offset: Offset(0.0, height), child: ConstrainedBox( constraints: BoxConstraints( minWidth: width, minHeight: 0, maxHeight: max( 0, MediaQuery.of(context).viewInsets.bottom - height - 15), ), child: new Card( child: Scrollbar( child: SingleChildScrollView( child: new Column( children: filteredSuggestions.map((suggestion) { return new InkWell( child: itemBuilder(context, suggestion), onTap: () { setState(() { if (submitOnSuggestionTap) { String Text = suggestion.toString(); textField.controller.text = Text; textField.focusNode.unfocus(); itemSubmitted(suggestion); if (clearOnSubmit) { clear(); } } else { String Text = suggestion.toString(); textField.controller.text = Text; textChanged(Text); } }); }); }).toList(), ), ), ))))); }); Overlay.of(context).insert(listSuggestionsEntry); }
filteredSuggestions = getSuggestions(
suggestions,
itemSorter,
itemFilter,
// suggestionsAmount,
query);
listSuggestionsEntry.markNeedsBuild();
}
List
suggestions = suggestions.where((item) => filter(item, query)).toList();
suggestions.sort(sorter);
return suggestions;
}
@override void dispose() { // if we created our own focus node and controller, dispose of them // otherwise, let the caller dispose of their own instances if (focusNode == null) { textField.focusNode.dispose(); } if (controller == null) { textField.controller.dispose(); } super.dispose(); }
@override Widget build(BuildContext context) { return CompositedTransformTarget(link: _layerLink, child: textField); } }
class SimpleAutoCompleteTextField extends AutoCompleteTextField
SimpleAutoCompleteTextField(
{TextStyle style,
InputDecoration decoration: const InputDecoration(),
this.onFocusChanged,
this.textChanged,
this.textSubmitted,
this.minLength = 1,
this.controller,
this.focusNode,
TextInputType keyboardType: TextInputType.text,
@required GlobalKey<AutoCompleteTextFieldState
@override
State
` Here's my working version of autocomplete textfield with scrollable, implemented from @tuarrep 's snippet. thank me later.
Any updates on this? Really need that feature
Here my quick and dirty, only-for-my-needs snippet. No time now to make it more configurable and push a PR
listSuggestionsEntry = OverlayEntry(builder: (context) { return Positioned( width: width, child: CompositedTransformFollower( link: _layerLink, showWhenUnlinked: false, offset: Offset(0.0, height), child: ConstrainedBox( constraints: BoxConstraints( minWidth: width, maxWidth: width, minHeight: 0, maxHeight: max(0, MediaQuery.of(context).viewInsets.bottom - height - 15)), child: Card( child: SingleChildScrollView( child: Column( children: filteredSuggestions.map((suggestion) { return Row(children: [ Expanded( child: InkWell( child: itemBuilder(context, suggestion), onTap: () { setState(() { if (submitOnSuggestionTap) { String Text = suggestion.toString(); textField.controller.text = Text; textField.focusNode.unfocus(); itemSubmitted(suggestion); if (clearOnSubmit) { clear(); } } else { String Text = suggestion.toString(); textField.controller.text = Text; textChanged(Text); } }); })) ]); }).toList(), )))))); }
Hope it helps
Currently, this is the best way. Thank you
Here is my working snippet If the above shared code is not working for you:
listSuggestionsEntry = OverlayEntry(builder: (context) {
return Positioned(
width: width,
child: CompositedTransformFollower(
link: _layerLink,
showWhenUnlinked: false,
offset: Offset(0.0, height),
child: SizedBox(
width: width,
height: 300,
child: SingleChildScrollView(
child: Card(
child: Column(
children: filteredSuggestions!.map((suggestion) {
return Row(children: [
Expanded(
child: InkWell(
child: itemBuilder!(context, suggestion),
onTap: () {
if (!mounted) return;
setState(() {
if (submitOnSuggestionTap) {
String newText = suggestion.toString();
textField!.controller!.text = newText;
if (unFocusOnItemSubmitted) {
textField!.focusNode!.unfocus();
}
itemSubmitted!(suggestion);
if (clearOnSubmit) {
clear();
}
} else {
String newText = suggestion.toString();
textField!.controller!.text = newText;
textChanged!(newText);
}
});
}))
]);
}).toList(),
)),
))));
});
PS: Above code is null safe and has lint issues resolved
Hi,
Your textfield is very usefull, but how could we make the suggestions scrollable ?
Thank you in advance,