I am using the typeAhead text field for auto-completion but the onSuggestionSelected function does not work. I am using flutter web. No selection is made and listView shows am hovering over an item above the one am actually hovering.
Below is the dialog, in this case the mouse pointer is actually below 'FIN' in the listView. I am using the typeAhead field in a dialog.
This is the code below.
TypeAheadFormField(
//initialValue: 'bleh',
textFieldConfiguration: TextFieldConfiguration(
controller: _typeAheadController, //this.
decoration: InputDecoration(
hintText: 'Programme(required)' //label
)),
suggestionsCallback: (pattern) {
return programme
.where((String dc) => dc
.toLowerCase()
.contains(pattern.toLowerCase()))
.toList(); //|| dc.toLowerCase().contains(other.toLowerCase())).toList();
},
itemBuilder: (context, String suggestion) {
return ListTile(
title: Text(suggestion),
);
},
transitionBuilder:
(context, suggestionsBox, controller) {
return suggestionsBox;
},
onSuggestionSelected: (String suggestion) {
_typeAheadController.text = suggestion; //this
print(suggestion + ' selected bith');
},
validator: (value) {
if (value.isEmpty ||
programme.indexOf(value) == -1) {
return 'Please select a course offered in the university you selected!';
}
},
onSaved: (value) => this._selectedCity =
value, //<--------- upload value
)
'programme' is the list of strings returned in which string 'dc' is found. 'print(suggestion + ' selected bith');' does not print a thing to show I never actually have onSelection run at all. Anyone know a work around? thank you.
Dialog:
You can use 2 methods to make it workable:
Method 1:
Use GestureDetector inside itemBuilder as below.
itemBuilder: (context, String suggestion) {
return GestureDetector(
onPanDown: (_) {
print(suggestion);
},
child: ListTile(
dense: true,
title: Text(suggestion),
),
);
},
by doing this, you still need the 'onSuggestionSelected' method, but you can leave it blank.
Method 2:
itemBuilder: (context, String suggestion) {
return Listener(
child: Container(
color: Colors.black, // <-- this gets rid of the highlight that's not centered on the mouse
child: ListTile(
title: Text(suggestion),
),
),
onPointerDown: () {
print(suggestion);
// Do what you would have done in onSuggestionSelected() here
},
);
},
I got these answers from following link onSuggestionSelected not called in Web
Related
I am working on a Flutter web app and created a TypeAheadFormField with a onSuggestionSelected function set to add an item to a list, which works only when I use arrow keys and press "Enter" but not when I left-click it using the mouse. I now also notice that the typeahead package on pub.dev does not list web as a supported platform which could be the cause of the issue but would still like to seek a second opinion.
Here's the snippet I'm working on for reference:
TypeAheadFormField(
textFieldConfiguration:
TextFieldConfiguration(
decoration:
const InputDecoration(
border:
OutlineInputBorder(
borderSide: BorderSide(
color:
projectsFieldOutline),
),
prefixIcon:
Icon(Icons.people)),
controller: collabController),
suggestionsCallback: (pattern) async {
List<String> matches =
collabSuggestions
.toSet()
.toList();
matches.retainWhere((element) =>
element.toLowerCase().startsWith(
pattern.toLowerCase()));
return matches;
},
itemBuilder:
(context, String suggestion) {
return ListTile(
title: Text(suggestion));
},
onSuggestionSelected:
(String suggestion) {
setState(() {
collabController.text = suggestion;
print(suggestion);
collabList
.add(suggestion.toString());
print(collabList);
});
},
)
To anyone who might face the same issue, I found a workaround that uses a Listener:
itemBuilder:
(context, String suggestion) {
return basic.Listener(
child: ListTile(
title: Text(suggestion),
),
onPointerDown: (_) {
// place logic from onSuggestionSelected here
},
);
},
I have a standard PopupMenuButton with CheckedPopupMenuItems:
PopupMenuButton<String>(
icon: const Icon(Icons.sort_by_alpha),
onSelected: (String result) {
if(result == optionOne){ doSomething(); }
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
CheckedPopupMenuItem<String>(
value: optionOne,
child: Text("optionOne"),
),
),
],
),
The checkmark for which item is selected appears to the left- I'd like to have it on the right, but can't find how to do this.
EDIT:
Is the only way really to not use CheckedPopupMenuItem completely and build the functionality myself?
I am making a growable Textfield widget that a user can add as many textfield as he wants.
I am using a list of textfields in this case. If user presses button to add textfield , it will be added to the list .
Also have created a function to delete the textfield if user wants and ima useing listName.removeAt() method for this . But when i delete a textfield which got some value there is a mismatch . I am deleting the textfield of that index but the value it holds shifts to another field.
Where i implement the code is :
Consumer(
builder: (ctx, ref, child) {
final customCourse =
ref.watch(customCourseTypeNotifierProvider);
return ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: customCourse.customCourses.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: MinimalInputField(
onChanged: (String value) {
},
hintText: "",
suffixIcon: IconButton(
icon: const Icon(Icons.remove),
color: Colors.red,
onPressed: () {
ref
.read(customCourseTypeNotifierProvider)
.removeCourse(index);
},
),
),
);
},
);
},
),
In type_controller.dart
class CourseTypeNotifier extends ChangeNotifier {
List<CustomCourseModel> customCourses = [];
void addCourse() {
customCourses.add(
const CustomCourseModel(title: "", description: ""),
);
notifyListeners();
}
void removeCourse(int index) {
customCourses.removeAt(index);
notifyListeners();
}
When text in MinimalInputField changes, the model is not updated and when the model is changed, text in MinimalInputField is not updated.
Using TextField could go something like this:
TextField(
controller: TextEditingController()..text = customCourse.customCourses[index].title!,
onChanged: (String value) {
ref.read(customCourseTypeNotifierProvider).updateCourse(index, value);
},
...
)
class CourseTypeNotifier extends ChangeNotifier {
...
void updateCourse(int index, String title) {
customCourses[index] = CustomCourseModel(title: title, description: "");
}
...
}
I'm using flutter Reactive Forms and I want to enable/disable a button dynamically while user is editing the textfield.
I saw in the Reactive Forms documentation they said like this:
Because of the two-binding capability of the ReactiveTextField with a
FormControl the widget don't include properties as controller,
validator, autovalidate, onSaved, onChanged, onEditingComplete,
onFieldSubmitted, the FormControl is responsible for handling
validation as well as changes notifications.
How do I listen changes while user is editing the textfield?
//Text Field
ReactiveFormConsumer(
builder: (ctx, form, child) => ReactiveTextField<String>(
formControlName: 'name',
controller: _nameController,
validationMessages: (control) => {
ValidationMessage.required: StringConst.namevalnotempty,
ValidationMessage.email: StringConst.sfs_incorrectName,
},
onSubmitted: () => form.valid
? _btnState = eButtonState.bActive
: _btnState = eButtonState.bDisable,
onEditingComplete: () {
form.valid
? _btnState = eButtonState.bActive
: _btnState = eButtonState.bDisable;
FocusScope.of(context).unfocus();
},
textInputAction: TextInputAction.next,
decoration: CommonStyle.textFieldStyle(
isFieldValid: form.control('name').valid),
),
)
//Save Button
ReactiveFormConsumer(
builder: (context, form, child) {
return SizedBox(
height: 48,
width: double.infinity,
child: ButtonWidget(
eButtonState: _btnState,
eButtonType: eButtonType.bText,
btnColor: CustomColors.green600,
borderColor: CustomColors.green600,
textColor: CustomColors.mWhite,
text: StringConst.saveChanges,
onPressed: () async {
var name = form.value["name"].toString();
setState(() {
_btnState = eButtonState.bLoading;
});
await editUserProfile(
name: name,
userType: userType,
);
},
),
);
},
),
According to the doc:
If you want to rebuild a widget each time a FormControl value changes you could use the ReactiveValueListenableBuilder widget.
ReactiveValueListenableBuilder will listen to the changes.
ReactiveValueListenableBuilder<double>(
formControlName: 'name',
builder: (context, value, child) {
// depending on the value enable/disable your button
},
),
#Minjin helped me to find the answer. I'm posting this for someone else who looking for full answer for the question.
ReactiveValueListenableBuilder<String>(
formControlName: 'name',
builder: (context, form, child) {
var name = form.value.toString();
var initialName = _userModel?.name?.toLowerCase();
// depending on the form value enable/disable your
// button, check field is edited
if (name != "" && name.toLowerCase() != initialName) {
_btnState = eButtonState.bActive;
} else {
_btnState = eButtonState.bDisable;
}
return SizedBox(
height: 48,
width: double.infinity,
child: ButtonWidget(
eButtonState: _btnState,
eButtonType: eButtonType.bText,
btnColor: CustomColors.green600,
borderColor: CustomColors.green600,
textColor: CustomColors.mWhite,
text: StringConst.saveChanges,
onPressed: () async {
},
),
);
},
)
Im trying to display suggestion in a textfield using typeahead but i receive an error"queryro is not a subtype of string"
Future<List<dynamic>> _getSuggestions(String query) {
return OrderProvider.db.rawQuery(
"select TypeOfProduct from Items where TypeOfProduct = ?", [query]);
}
**This is the code I use to fetch data from database **
TypeAheadFormField(
textFieldConfiguration: TextFieldConfiguration(
controller: this._typeController,
autocorrect: true,
autofocus: true,
decoration: InputDecoration(hintText: "Product Name"),
),
suggestionsCallback: (suggestion) async {
return _getSuggestions(suggestion);
},
itemBuilder: (context, suggestion) {
return ListTile(
title: Text(suggestion),
);
},
onSuggestionSelected: (suggestion) {
this._typeController.text = suggestion;
},
),
OrderProvider.db.rawQuery(
"select TypeOfProduct from Items where TypeOfProduct = ?", [query]);
This return a List<Map> (If you're using sqfLite) and suggestionsCallback will pass that List to the itemBuilder, but itemBuilder is using those suggestions to build a Text Widget (and Text expects something of type String, not of type Map). so you can reduce the Map before using return in the _getSuggestions(String query) method to return a List or do something like this
itemBuilder: (context, suggestion) {//suggestion is of type Map because thats what you returned
return ListTile(
title: Text(suggestion['product']), //or whatever the key name is
);
},