I want to autocomplete on items in FormBuilderDropdown. When I write a letter, the item list changes to show the items that contain that letter.
FormBuilderDropdown(
attribute: 'field_city',
decoration: LikpechInputDecoration(_isLoading, 'Selectionnez une ville'),
items: _buildListCities(),
/*initialValue: DropdownMenuItem(
value: _selectedCity,
child: Text(_listCities[_selectedCity]['name']),
),*/
onChanged: (index) {
setState(() {
_selectedCity = index;
});
fetchAgences(index);
},
validators: [
FormBuilderValidators.required(errorText: '')
],
) : Center(child: CupertinoActivityIndicator()),
use typehead plugin typehead flutter
import 'package:flutter_typeahead/flutter_typeahead.dart';
.
.
.
TypeAheadField(
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
style: DefaultTextStyle.of(context).style.copyWith(
fontStyle: FontStyle.italic
),
decoration: InputDecoration(
border: OutlineInputBorder()
)
),
suggestionsCallback: (pattern) async {
return await BackendService.getSuggestions(pattern);
},
itemBuilder: (context, suggestion) {
return ListTile(
leading: Icon(Icons.shopping_cart),
title: Text(suggestion['name']),
subtitle: Text('\$${suggestion['price']}'),
);
},
onSuggestionSelected: (suggestion) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ProductPage(product: suggestion)
));
},
)
.
.
.
Related
I am using GetX in my app and I am also a beginner to GetX. I have some code in my controller and my UI listens to that state by wrapping around it with a Obx(() => myWidget). Eventually my UI does not update even when my state changes and I am printing the changes in my console too.
This is my controller
class MyController extends GetxController {
RxList<String> topicsDummyList = <String>[
'Career',
'computer',
'science',
'art',
'creative',
'axim',
].obs;
RxList<String> searchTopicsList = <String>[].obs;
void searchTopicsListHandler(String val) {
if (!val.isBlank!) {
searchTopicsList.value = topicsDummyList
.where(
(String e) => e.toLowerCase().contains(
val.toLowerCase(),
),
)
.toList();
}
}
}
This is my View(UI) too
Obx(() {
return customFilterField(
type: MentorFilterType.TOPIC,
hintText: 'Topic/Mentorship focus',
selectedList: controller.selectedTopicsList,
searchHintText: 'Search topics',
// THIS IS WHERE I AM LISTENING TO THE STATE
dummyList: controller.searchTopicsList.isEmpty
? controller.topicsDummyList
: controller.searchTopicsList,
onEnd: (_) {
controller.searchTopicsList.clear();
});
}),
THIS IS THE customFilterField ITS SELF
Widget customFilterField({
required String hintText,
required List selectedList,
required String searchHintText,
required List dummyList,
required MentorFilterType type,
required Function(dynamic result)? onEnd,
}) {
return Obx(
() => CustomTextFieldWidget(
key: ValueKey(selectedList),
onTap: () {
Utils.customBottomSheet(
onEnd: onEnd,
child: Obx(
() => Column(
children: [
verticalSpaceSmallY,
CustomTextFieldWidget(
onChanged: (val) {
controller.searchTopicsListHandler(
val!,
);
}
hintText: searchHintText,
prefixIcon: SvgPicture.asset(AppImages.searchSvg),
fillColor: Get.theme.scaffoldBackgroundColor,
borderColor: AppColors.tinygrey,
borderRadius: 30,
validator: null,
).paddingSymmetric(
horizontal: 18.w,
),
ListView.builder(
padding: EdgeInsets.only(top: 18.w),
itemCount: dummyList.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Obx(
() => CustomListTileWidget(
contentpadding:
EdgeInsets.symmetric(horizontal: 18.w),
onTap: () {
controller.topicsSelectedHandler(
dummyList[index],
);
}
titleWidget: CustomText(
text: dummyList[index],
font: Dimens.fontSize15,
txtAlign: TextAlign.justify,
fntweight: FontWeight.w400,
),
trailing: Container(
height: 15,
width: 15,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: selectedList.contains(dummyList[index])
? null
: Border.all(),
),
child: selectedList.contains(dummyList[index])
? SvgPicture.asset(
AppImages.select,
)
: null,
),
),
);
},
),
verticalSpaceMediumX,
CustomElevatedButton(
onPressed: () {
Get.back();
},
title: 'Continue',
),
],
),
),
);
},
initialValue: selectedList.length == 1
? selectedList.first
: selectedList.length >= 2
? '${selectedList.length} topics selected'
: null,
readOnly: true,
borderRadius: 5,
hintText: hintText,
),
);
}
I am using the CupertinoSearchTextField in my app. It is working fine so far but I am missing one feature: the Cancel-Button.
In native iOS you can set to show the button which looks like this:
Does Flutter provide this functionality? I couldn't find it anywhere.
Clarification:
I don't mean the x/clear-button. I know that is build-in. What I mean is the actual Cancel-button which removes focus from the textField.
use Typeahead package.then in suffixIcon, you can add cancel feature to clear field.
TypeAheadField<String>(
hideOnEmpty: true,
minCharsForSuggestions: 2,
getImmediateSuggestions: true,
textFieldConfiguration: TextFieldConfiguration(
controller: cont_search,
cursorColor: Colors.grey,
textInputAction: TextInputAction.search,
decoration: InputDecoration(
//here the cancel button
suffixIcon: IconButton(
padding: EdgeInsets.fromLTRB(8, 4, 8, 8),
icon: Icon(Icons.clear),
onPressed: (){
cont_search.clear();
},
),
focusColor: Colors.black,
focusedBorder: InputBorder.none,
border: InputBorder.none,
//hintText: 'What are you looking for?',
icon: Icon(Icons.search),
),
onSubmitted: (value){
print("value taken is ${value}");
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => search_page(value)
));
}
),
suggestionsCallback: (String pattern) async {
return matches
.where((item) =>
item.toLowerCase().startsWith(pattern.toLowerCase()))
.toList();
},
itemBuilder: (context, String suggestion) {
return ListTile(
title: Text(suggestion),
);
},
onSuggestionSelected: (String suggestion) {
//push to page
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => search_page(suggestion)
));
print("Suggestion selected ${suggestion}");
},
)
If you wanna override x/clear-button's behaviour to unfocus the textfield, use this. Otherwise, you can put search textfield and a clear button in a row and implement button's behaviour like this. Problem solved.
onSuffixTap: (){
FocusScope.of(context).unfocus();
}
I ended up building it myself. I made use of the Focus-Widget and most important the AnimatedPadding. My code looks like this:
Row(
children: [
Flexible(
child: AnimatedPadding(
duration: const Duration(milliseconds: 100),
padding: EdgeInsets.only(right: _isSearching ? 50 : 0),
child: Focus(
onFocusChange: (hasFocus) {
setState(() {
_isSearching = hasFocus;
});
},
child: CupertinoSearchTextField(
placeholder: 'Suche'.tr,
controller: _textEditingController,
focusNode: _focusNode,
),
),
),
),
if (_isSearching)
Tappable(onTap: () {
dismissKeyboard();
}, builder: (context, isTapped) {
return AnimatedText(
text: 'Abbrechen',
isTapped: isTapped,
style: AppTextStyles.avenirNextH4Regular,
color: grey,
);
}),
],
),
I have a dropdownFormField which takes data from snapshot and working fine.
now the data has grown bigger, as such want to change it to autocompleteFormField.
The code for dropdownFormField I am using is like this:
Container(
height: 50.0,
padding: EdgeInsets.only(
left: 15, right: 15, top: 5),
child: DropdownButtonHideUnderline(
child: ButtonTheme(
child: FutureBuilder(
future: _testkit,
builder: (context,
AsyncSnapshot<TestkitList> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(
child:
CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'error',
// '${snapshot.error}',
style: TextStyle(
color: Colors.red),
);
} else {
return DropdownButtonFormField<
String>(
hint:
Text("Select Testkit Name"),
value: _selectedTestkit,
onChanged: (newValue) async {
_selectedTestkit = newValue;
_selectedTestType =
await getTestType();
setState(() {});
print(
"the below line is printed in dropdownfield");
print(_selectedTestType);
},
validator: (value) => value ==
null
? 'Please select the Testkit'
: null,
items: (snapshot.data.data)
.map((item) =>
DropdownMenuItem<
String>(
child: Text(
item.attributes.name
.length >
30
? item
.attributes
.name
.substring(
0, 30)
: item
.attributes
.name,
),
value: item.id,
))
.toList(),
);
}
}
}),
)),
),
Now plugin example for autocompleteFormField is like below:
SimpleAutocompleteFormField<Person>(
decoration: InputDecoration(labelText: 'Person', border: OutlineInputBorder()),
suggestionsHeight: 80.0,
itemBuilder: (context, person) => Padding(
padding: EdgeInsets.all(8.0),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(person!.name, style: TextStyle(fontWeight: FontWeight.bold)),
Text(person.address)
]),
),
onSearch: (search) async => people
.where((person) =>
person.name.toLowerCase().contains(search.toLowerCase()) ||
person.address.toLowerCase().contains(search.toLowerCase()))
.toList(),
itemFromString: (string) {
final matches = people.where((person) => person.name.toLowerCase() == string.toLowerCase());
return matches.isEmpty ? null : matches.first;
},
onChanged: (value) => setState(() => selectedPerson = value),
onSaved: (value) => setState(() => selectedPerson = value),
validator: (person) => person == null ? 'Invalid person.' : null,
)
somehow I am not able to make it pickup and work as whether I am using classname TestkitList' or snapshot.data.data' replacing the person which is in my case is a future.
You could flutter-typeahead package
https://pub.dev/packages/flutter_typeahead/install
TypeAheadField(
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
style: DefaultTextStyle.of(context).style.copyWith(
fontStyle: FontStyle.italic
),
decoration: InputDecoration(
border: OutlineInputBorder()
)
),
suggestionsCallback: (pattern) async {
return await BackendService.getSuggestions(pattern);
},
itemBuilder: (context, suggestion) {
return ListTile(
leading: Icon(Icons.shopping_cart),
title: Text(suggestion['name']),
subtitle: Text('\$${suggestion['price']}'),
);
},
onSuggestionSelected: (suggestion) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ProductPage(product: suggestion)
));
},
)
I have a DropdownButtonFormField where the last item is a DropdownMenuItem to add a new Object using a Dialog.
Padding(
padding: const EdgeInsets.only(bottom: 15),
child: Observer(
builder: (_){
return DropdownButtonFormField(
value: createdContentStore.subjectTitleSelected,
isDense: true,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
isDense: true,
border: OutlineInputBorder()
),
onChanged: (value) async {
// print(value);
if(value == 'newSubject'){
Subject newSubject = await showDialog(
context: context,
builder: (_) => CreatedSubjectDialogBox(isNewContent: true,)
);
if(newSubject != null){
createdContentStore.setSubjectTitleSelected(newSubject.title);
createdContentStore.setSubject(newSubject);
} else {
// WHAT CAN I DO HERE TO RESET DROP'S VALUE?
}
} else {
createdContentStore.setSubjectTitleSelected(value);
}
},
iconSize: 30,
hint: Text('Selecione uma matéria'),
items: subjectStore.subjectList.map((subject) => DropdownMenuItem(
value: subject.title,
child: Text(subject.title),
onTap: () {
createdContentStore.setSubject(subject);
},
)).toList()..add(DropdownMenuItem(
value: 'newSubject',
child: Center(
child: Text(
'Nova Matéria'.toUpperCase(),
style: TextStyle(color: redRevise),
),
),
)),
);
},
),
);
When the Dialog is shown the user can create a new Object that will appear in the Dropdown. When the user cancels the Dialog it is showing the last item. The desired behavior is to show the hint instead.
Can someone help me?
Thank you!
All you have to do is remove the value from the drop down,
DropdownButtonFormField(
//** REMOVE THE VALUE **
isDense: true,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
isDense: true,
border: OutlineInputBorder()
),
onChanged: (value) async {
if(value == 'newSubject'){
Subject newSubject = await showDialog(
context: context,
builder: (_) => CreatedSubjectDialogBox(isNewContent: true,)
);
if(newSubject != null){
createdContentStore.setSubjectTitleSelected(newSubject.title);
createdContentStore.setSubject(newSubject);
} else {
// WHAT CAN I DO HERE TO RESET DROP'S VALUE?
}
} else {
createdContentStore.setSubjectTitleSelected(value);
}
},
iconSize: 30,
hint: Text('Selecione uma matéria'),
items: subjectStore.subjectList.map((subject) => DropdownMenuItem(
value: subject.title,
child: Text(subject.title),
onTap: () {
createdContentStore.setSubject(subject);
},
)).toList()..add(DropdownMenuItem(
value: 'newSubject',
child: Center(
child: Text(
'Nova Matéria'.toUpperCase(),
style: TextStyle(color: redRevise),
),
),
)),
);
},
),
);
I currently have the following widget and I need to add a new button to it. However, the buttons show up one below the other instead of side by side. I tried to use Row instead of Column as well & that messed up the entire page. Any ideas on how to get these buttons side by side? Thank you!!
Widget getRequestAmount() {
return AppTextField(
padding: EdgeInsets.symmetric(horizontal: 30),
focusNode: _transferAmountNode,
controller: _transferAmountController,
keyboardType: TextInputType.number,
hintText: 'Request Amount',
inputFormatters: [
/* CurrencyFormatter(
currency: _selectedAccount?.currency ?? '') */
],
onSubmitted: (value) {
setState(() {
this.showQR = true;
});
Navigator.pop(context);
Sheets.showAppHeightEightSheet(
context: context,
widget: Column(children: <Widget>[
getBuyerAvatar(),
showAmount(),
_contentWidget(),
Container(margin: const EdgeInsets.only(top: 5, bottom: 5)),
AppButton.buildAppButton(
context,
AppButtonType.TEXT_OUTLINE,
'Button 1',
Dimens.BUTTON_BOTTOM_DIMENS, onPressed: () async {
Uint8List generateImgFromQR = await _captureAndCreateQRPng();
Sheets.showAppHeightEightSheet(
context: context,
widget: generatePDF(generateImgFromQR),
);
}),
AppButton.buildAppButton(
context,
AppButtonType.TEXT_OUTLINE,
'Second Button',
Dimens.BUTTON_BOTTOM_DIMENS, onPressed: () async {
moneyAmount = _transferAmountController.text;
Sheets.showAppHeightEightSheet(
context: context,
widget: generatePDF(generateImgFromQR),
);
})
]));
},
);
}
wrap only two buttons in a row Widget
Widget getRequestAmount() {
return AppTextField(
padding: EdgeInsets.symmetric(horizontal: 30),
focusNode: _transferAmountNode,
controller: _transferAmountController,
keyboardType: TextInputType.number,
hintText: 'Request Amount',
inputFormatters: [
/* CurrencyFormatter(
currency: _selectedAccount?.currency ?? '') */
],
onSubmitted: (value) {
setState(() {
this.showQR = true;
});
Navigator.pop(context);
Sheets.showAppHeightEightSheet(
context: context,
widget: Column(children: <Widget>[
getBuyerAvatar(),
showAmount(),
_contentWidget(),
Container(margin: const EdgeInsets.only(top: 5, bottom: 5)),
Row(
children: <Widget>[
AppButton.buildAppButton(
context,
AppButtonType.TEXT_OUTLINE,
'Button 1',
Dimens.BUTTON_BOTTOM_DIMENS, onPressed: () async {
Uint8List generateImgFromQR = await _captureAndCreateQRPng();
Sheets.showAppHeightEightSheet(
context: context,
widget: generatePDF(generateImgFromQR),
);
}),
AppButton.buildAppButton(
context,
AppButtonType.TEXT_OUTLINE,
'Second Button',
Dimens.BUTTON_BOTTOM_DIMENS, onPressed: () async {
moneyAmount = _transferAmountController.text;
Sheets.showAppHeightEightSheet(
context: context,
widget: generatePDF(generateImgFromQR),
);
})
])
]));
},
);
}
For this you can use the Row widget. Which works exactly like the Column Widget, which you already use, but it is oriented horizontal.
You can read more about this widget here.
Your code would then look like:
Widget getRequestAmount() {
return AppTextField(
padding: EdgeInsets.symmetric(horizontal: 30),
focusNode: _transferAmountNode,
controller: _transferAmountController,
keyboardType: TextInputType.number,
hintText: 'Request Amount',
inputFormatters: [
/* CurrencyFormatter(
currency: _selectedAccount?.currency ?? '') */
],
onSubmitted: (value) {
setState(() {
this.showQR = true;
});
Navigator.pop(context);
Sheets.showAppHeightEightSheet(
context: context,
widget: Column(
children: <Widget>[
getBuyerAvatar(),
showAmount(),
_contentWidget(),
Container(margin: const EdgeInsets.only(top: 5, bottom: 5)),
Row(
children: [
AppButton.buildAppButton(
context,
AppButtonType.TEXT_OUTLINE,
'Button 1',
Dimens.BUTTON_BOTTOM_DIMENS, onPressed: () async {
Uint8List generateImgFromQR =
await _captureAndCreateQRPng();
Sheets.showAppHeightEightSheet(
context: context,
widget: generatePDF(generateImgFromQR),
);
}),
AppButton.buildAppButton(
context,
AppButtonType.TEXT_OUTLINE,
'Second Button',
Dimens.BUTTON_BOTTOM_DIMENS, onPressed: () async {
moneyAmount = _transferAmountController.text;
Sheets.showAppHeightEightSheet(
context: context,
widget: generatePDF(generateImgFromQR),
);
})
],
),
],
),
);
},
);
}