I have created a simple demo page for learning firebase,
here I have a textfield, searchbutton and StreamBuilder for showing all records
I have placed a condition for showing all records when textfield is empty...
here I have used a button which just rebuild...just used setState inside it onpressed
everything working well, but I just want to improve that record should be filter while typing on textfield so that no need of button there.
how to call StreamBuilder's stream in textfield change.... and in addition, I have created customTextField,,so how to pass function as onChange requires a value...
here is my coding
Column(
children: [
SizedBox(
height: 40,
),
CustomTextField(
hint: 'Search Email', controller: txtsearchcontroller),
SizedBox(
height: 20,
),
CupertinoButton(
child: Text('Search'),
onPressed: () {
setState(() {});
}),
SizedBox(
height: 30,
),
StreamBuilder(
stream: txtsearchcontroller.text == ''
? FirebaseFirestore.instance
.collection('chatusers')
.snapshots()
: FirebaseFirestore.instance
.collection('chatusers')
.where(
"email",
isEqualTo: txtsearchcontroller.text,
isNotEqualTo: widget.usermodel.email,
)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
QuerySnapshot querysnapshot =
snapshot.data as QuerySnapshot;
if (querysnapshot.docs.length > 0) {
//and suggest me if any better way instead of this following
List<Map<String, dynamic>> maplist = [];
for (int x = 0; x < querysnapshot.docs.length; x ) {
Map<String, dynamic> usermap = querysnapshot.docs[x]
.data() as Map<String, dynamic>;
maplist.add(usermap);
}
return Expanded(
child: CustomListView(maplist:maplist),
);
} else {
return Text('Has Data but No user Found');
}
} else {
if (snapshot.hasError) {
return Text('error found');
} else {
return Text('empty..');
}
}
} else {
return CircularProgressIndicator();
}
}),
here is my CustomTextField
class CustomTextField extends StatelessWidget {
final String hint;
final bool isitpassword;
final TextEditingController controller;
const CustomTextField({Key? key,required this.hint,this.isitpassword=false,required this.controller}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(20),
),
child: TextField(
style: TextStyle(
fontSize: 20,color: Colors.white,),
controller: controller,
obscureText: isitpassword,
decoration: InputDecoration(
border: InputBorder.none,
hintText: hint,
suffixIcon: IconButton(icon: Icon(Icons.close,color: Colors.white,),onPressed: (){
controller.text='';
},),
),
)),
);
}
}
CodePudding user response:
TextField
widget has an onChanged
property, which will execute every time the input is changed.
in the CustomTextField
, add the onChanged
property :
TextField(
onChanged: (value) { SetState(() {})},
/* your other properties ...*/
),
this will give you what you need, but since you're calling CustomTextField
which contains that TextField
, pass the onChanged method from the constructor so you can use it like this:
CustomTextField(
onChanged: () { SetState(() {})},
hint: 'Search Email', controller: txtsearchcontroller),
SizedBox(
height: 20,
),
now every time the input changes, a SetState(() {})
will be executed causing rebuild.
CodePudding user response:
You can use StreamController for this purpose. For example,
enum SearchState {
INITIAL,
LOADING,
LOADED,
}
StreamController<SearchState> searchController = StreamController.broadcast()..add(SearchState.INITIAL);
List<ChatUser?> searchResult = []; // your filtered results
List<ChatUser?> users = []; // all users
void searchOperation({required String searchText}) {
searchController.add(SearchState.LOADING); // User is typing
searchResult.clear();
for (var element in users) {
final word = element?.username?.trim();
if (word?.toLowerCase().contains(searchText.toLowerCase()) == true) { // your equality logic here
searchResult.add(element);
}
}
searchController.add(SearchState.LOADED);
}
Then you should give a searchController to StreamBuilder ,
StreamBuilder<SearchState>(
stream: searchController.stream,
builder: (context, snapshot) {
if (snapshot.data == SearchState.LOADING) { // draw your ui ..
return const Center(
child: CircularProgressIndicator(),
);
}...
Maybe you may want to check full example on my repository here , https://github.com/SamedHrmn/flutter-playgrounds/tree/master/textfield_searching