How to add search history functionality in flutter app - flutter

I have recently add search functionality in flutter app and I want to add search history functionality in my flutter app. I am stuck in this I have no idea that how to add it.
Please give me instuction and also provide suitable code for it .
My code
TextFormField(
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,
controller: searchcontroller,
focusNode: fousnode,
onChanged: (query) {
setState(() {
FirebaseFirestore.instance
.collectionGroup("Add_product")
.where("product_name", isGreaterThanOrEqualTo: query)
.where("product_name", isLessThan: query + 'z')
.snapshots();
});
},
decoration: InputDecoration(
hintText: "Search Product",
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(11)),
),
),
)
StreamBuilder(
stream:
FirebaseFirestore.instance.collectionGroup("Add_product").snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
List<QueryDocumentSnapshot> data = snapshot.data!.docs;
if (searchcontroller.text.isNotEmpty) {
data = data.where((doc) => doc["product_name"]
.toLowerCase()
.contains(searchcontroller.text.toLowerCase()))
.toList();
}
return GridView.builder(
itemCount:data.length,
itemBuilder: (itemBuilder, index) {
return Container(
child: InkWell(
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (builder)=>detail(
url: snapshot.data!.docs[index]["url"],
productName: snapshot.data!.docs[index]["product_name"],
productPrice: snapshot.data!.docs[index]["product_price"],
)));
},
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(
snapshot.data!.docs[index]["url"]
),
),
title: Text(snapshot.data!.docs[index]["product_name"])
),
),
),
);
},
);
}),

every time a search is performed you can create a list or a map and save it locally or in the database according to your needs. To then make the data that you are going to recover appear under the search bar, I recommend using existing plugins such as material_floating_search_bar
example with shared preferences (locally):
final prefs = await SharedPreferences.getInstance();
List<String> _currentResearch = prefs.getStringList("searches") ?? [];
_currentResearch.add(searchcontroller.text);
await prefs.setStringList("searches", _ricercheAttuali);

Related

Search Term not updating from TextField to Firestore StreamBuilder Call

I am trying to implement a place where users in my app can search my Firestore Database.
When The user enters a search term into the textField:
return Scaffold(
appBar: AppBar(
title: TextField(
textInputAction: TextInputAction.search,
onSubmitted: (value) {
setState(() {
searchKey = value;
streamQuery = db
.collection('GearLockerItems')
.where('searchTerm', isGreaterThanOrEqualTo: searchKey)
.where('searchTerm', isLessThan: '${searchKey}z')
.snapshots();
});
},
decoration: const InputDecoration(
hintText: 'Search for Gear',
prefixIcon: Icon(Icons.search),
),
),
)
I take the value for the search term and place it into the streamQuery
I then take that streamQuery and put it into the StreamBuilder:
body: StreamBuilder<dynamic>(
stream: streamQuery,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
//widget build for complated call
print('Inside stream $searchKey');
if (snapshot.hasData) {
return ListView.builder(
AS you can see I put a print statement in there for testing and Inside Stream $searchKey keeps showing null, which is was I default it to. I do not understand why when I enter data into the search box its not updating the searchKey to what ever I type and is keeping it null...
return Scaffold(
appBar: AppBar(
title: TextField(
textInputAction: TextInputAction.search,
onSubmitted: (value) {
searchKey = value;
await db
.collection('GearLockerItems')
.where('searchTerm', isGreaterThanOrEqualTo: searchKey)
.where('searchTerm', isLessThan: '${searchKey}z')
.snapshots().then((data) { setstate((){streamQuery = data; });});
},
decoration: const InputDecoration(
hintText: 'Search for Gear',
prefixIcon: Icon(Icons.search),
),
),
)
First fetch the data from firestore and use then function to set state.May it works and don't forget to use await because you wait untill the data is fetched.

Flutter - How to reset Autocomplete list after fetching data from server?

I have Autocomplete list:
List<CompanyName> companyNames = <CompanyName>[
const CompanyName(name: 'No Data'),
];
And this works, only one item No Data is on the array, but that array is filled by data from the server, and the problem is when you press autocomplete on start you will see the No Data item on the list, after server fetching data, the list will not be updated by data from the server.
My idea is to create a local variable that will be updated by the async call, and that variable should hide autocomplete list before the server responds, or refresh the (re-render) widget after fetching...
Autocomplete<CompanyName>(
optionsBuilder:
(TextEditingValue textEditingValue) {
return companyNames.where((CompanyName companyName) {
return companyName.name.toLowerCase().contains(textEditingValue.text.toLowerCase());
}).toList();
},
optionsViewBuilder: (BuildContext context, AutocompleteOnSelected<CompanyName>
onSelected,
Iterable<CompanyName> options) {
return Align(
alignment: Alignment.topLeft,
child: Material(
child: ConstrainedBox(constraints: const BoxConstraints(maxHeight: 280,),
child: SizedBox(width: 280,
height: companyNames.length <= 1 ? 80 : 280,
child: ListView.builder(padding: const EdgeInsets.all(10.0),
itemCount: options.length, itemBuilder: (BuildContext context, int index) { final CompanyName option = options.elementAt(index);
return GestureDetector(
onTap: () { onSelected(option); },
child: ListTile( title: Text(option.name, style: TextStyle(color: isDarkMode ? Colors.white : Colors.black)),
),
);
})))));
},
fieldViewBuilder:
(context,
controller,
focusNode,
onEditingComplete) {
return TextFormField(
controller:
controller,
focusNode:
focusNode,
onEditingComplete:
onEditingComplete,
keyboardType:
TextInputType
.text,
autocorrect:
false,
decoration: InputDecoration(
isDense: true,
hintText: "Company Name",
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(10.0),
),
fillColor: isDarkMode ? const Color(0XFF212124) : Colors.white,
filled: true),
validator: (value) {
if (value ==
null ||
value.isEmpty) {
return 'Please enter company name';
} else {
setState(
() {
client =
value;
});
}
return null;
});
},
onSelected:
(CompanyName
selection) {
setState(() {
brokerCompany =
selection
.name;
});
},
displayStringForOption:
(CompanyName
option) =>
option
.name,
),
What is the best option and where is the best option to put the variable and re-render Autocomplete()?

I have to go to this screen twice to load data from fetchAndSet in flutter

When I visited the page first time, it just shows circularProgressIndicator. After going back when I visit that page second time, then circularProgressIndicator shows up and other listview.builder appears perfectly. I have updated the code in question. Any kind of help is much appreciated.
FutureBuilder(
future: Provider.of<AppointmentsProvider>(context, listen: false)
.fetchAndSetAvailableSlots(),
builder: (ctx, snapShot) => Column(
children: [
Container(
margin: EdgeInsets.all(5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.blue,
),
child: SizedBox(
height: 380,
child: snapShot.connectionState == ConnectionState.waiting
? Center(
child: CircularProgressIndicator(
color: Colors.white,
),
)
: ListView.builder(
itemCount: list.length,
itemBuilder: (ctx, i) => Card(
elevation: 5,
margin: EdgeInsets.all(10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
child: CheckboxListTile(
secondary: Icon(
Icons.alarm,
),
activeColor: Colors.green,
// checkColor: Colors.black,
selectedTileColor: Colors.blue,
// tileColor: Colors.greenAccent,
title: Text(list[i].time),
value: list[i].isSelected,
onChanged: (value) {
onCheckChanged(list[i], list, value);
},
),
),
),
),
),
ElevatedButton.icon(
onPressed: () async {
await Firebase.initializeApp();
FirebaseFirestore.instance.collection('appointments').add({
'id': DateTime.now().toString(),
'date': widget.formatDate,
'description': 'description etc.',
'type': 'type etc.',
'uid': 'uid etc.',
'timeSlot': timeSlot,
});
},
icon: Icon(Icons.arrow_forward),
label: Text('Add Now!'),
),
],
),
);
---here is the fetchAndSet function---
Future<void> fetchAndSetAvailableSlots() async {
await Firebase.initializeApp();
QuerySnapshot<Map<String, dynamic>> data = await FirebaseFirestore.instance
.collection('appointments')
.where('date', isEqualTo: 'Dec 11, 2021')
.get();
List<TimeSlot> bookedSlots = [];
data.docs.forEach(
(element) async {
FirebaseFirestore.instance.collection('Questions').doc(userId).get();
DocumentSnapshot<Map<String, dynamic>> item = await FirebaseFirestore
.instance
.collection('appointments')
.doc('${element.id}')
.get();
bookedSlots
.add(TimeSlot(isSelected: false, time: item.data()['timeSlot']));
},
);
availableTimeSlots = bookedSlots;
print(bookedSlots);
notifyListeners();
}
first of all I would suggest moving
Firebase.initializeApp();
Into your runApp function.
Regarding your second problem it looks like you are using Provider. If so I would suggest passing an bool loading determining wether or not await getting data is still running. Also a Futurebuilder/Streambuilder could be helpful in displaying that data. Your problem sounds like the widget doesnt rebuild if data is fetched
Before returning the ListView.builder check the list. If the list is empty then return CircularProgressIndicator() and if the list get some data then return the listview. On the other hand add data to bookSlots list inside setState().

Flutter Dynamic Textfield Autocompletion

I use the library autocomplete_textfield to create a textfield with autocompletion. Every time the text is changed I make a request to fetch a specific list of users and then set them to the suggestion of my AutocompleteTextfield. The list seems to be updated (when I print(list.length)) but visually it isn't. Any idea?
My AutocompletionTextfield:
AutoCompleteTextField<User>(
textChanged: (item) async {
await model.searchRecommendation(item);
},
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(0)
),
labelText: 'Recommendations',
labelStyle: TextStyle(color: AppColors.blackColor)
),
key: autocompleteUserSearchTextFieldKey,
suggestionsAmount: 3,
controller: _userSearchController,
itemSubmitted: (item) {},
suggestions: model.userRecommendations,
itemBuilder: (context, suggestion) => new Padding(
padding: EdgeInsets.all(16),
child: ListTile(
title: Text(suggestion.email)
),
),
itemSorter: (a, b) => a.email.compareTo(b.email),
itemFilter: (suggestion, input) => suggestion.email.toLowerCase().startsWith(input.toLowerCase()),
),
my viewmodel:
List<User> userRecommendations = [];
Future searchRecommendation(String filter) async {
var token = await SharedPreferenceUtils().getStringValue('jwt');
final response = await _api.filterUserSearch(filter, currentUser, token);
if (response is SuccessState) {
List<dynamic> tmp = response.value.payload;
tmp ??= [];
userRecommendations = List<User>.from(tmp.map((x) => User.fromJson(x)));
notifyListeners();
} else if (response is ErrorState) {
String error = response.msg;
print('Error $error');
} else {
print('Error');
}
}
Nvm I found the answer! AutocompleteTextfield has a method called updateSuggestions(). I just had to use it.
I don't see all of your code, but I assume the widget tree isn't being rebuilt. You have to wrap AutoCompleteTextField with FutureBuilder or StreamBuilder (depending on your implementation, anyway StreamBuilder seems more appropriate there) that listens to recommendations result.
You could also implement StatefulWidget or use StatefulBuilder.
Example with StatefulBuilder (it's dirty but should work, ideally you'd use StreamBuilder):
return StatefulBuilder(
builder: (context, setState) => AutoCompleteTextField<User>(
textChanged: (item) async {
await model.searchRecommendation(item);
setState(() {});
},
decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.circular(0)),
labelText: 'Recommendations',
labelStyle: TextStyle(color: AppColors.blackColor)),
key: autocompleteUserSearchTextFieldKey,
suggestionsAmount: 3,
controller: _userSearchController,
itemSubmitted: (item) {},
suggestions: model.userRecommendations,
itemBuilder: (context, suggestion) => new Padding(
padding: EdgeInsets.all(16),
child: ListTile(title: Text(suggestion.email)),
),
itemSorter: (a, b) => a.email.compareTo(b.email),
itemFilter: (suggestion, input) => suggestion.email.toLowerCase().startsWith(input.toLowerCase()),
),
);

How to add text on dropdownmenu?

I'm making an app using Flutter and I want to add text for my DropDownMenu like Select car make or something like that.
I could not find a tutorial so was hoping someone could help.
Here is my code:
new FormField(builder: (FormFieldState state) {
return InputDecorator(
decoration: InputDecoration(
),
child:FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
final album = snapshot.data;
final results = album.results;
return DropdownButtonHideUnderline(
child: DropdownButton<Results>(
isExpanded: true,
items: results.map((result) {
return DropdownMenuItem<Results>(
value: result,
child: Text('${result.modelName}'),
);
}).toList(),
onChanged: (album ) {
// selected album
setState(() {
_selected = album;
});
},
value: _selected,
));
} else
return CircularProgressIndicator();
}),
);
}),
I tried to set hinttext, but it does not work.
Here is how you can add a hint to your DropdownButton:
hint: Container(
width: 150,
child: Text(
"Select car",
style: TextStyle(color: Colors.grey),
textAlign: TextAlign.end,
),
),