I'm using StreamBuilder to show a group of elements from Firebase in a list. The problem is that I need to filter and update that list changing the stream from Streambuilder. I don't know who can I do that. I read about Streamcontroller and I tried to use it but it throws me an error ("StateError (Bad state: Cannot add new events while doing an addStream)"). Please, I don't know what to do :( Thank you so much!
Here is the important parts of the code:
class _widget_productosState extends State<widget_productos> {
final User? usuario = FirebaseAuth.instance.currentUser;
final firestoreInstance = FirebaseFirestore.instance;
List<dynamic> lista_alergenos = [];
var nombre_filtro = TextEditingController();
var nombre = '';
Stream<QuerySnapshot> stream_datos = FirebaseFirestore.instance.collection('Producto').snapshots();
StreamController<QuerySnapshot> controller = StreamController<QuerySnapshot>.broadcast();
@override
void initState() {
super.initState();
stream_datos.forEach((element) {
controller.add(element);
});
}
@override
Widget build(BuildContext context) {
List<String> filtro_alergenos = [];
return
SafeArea(child:
Scaffold(
backgroundColor: Color.fromARGB(255, 239, 241, 245),
body: StreamBuilder(
stream: controller.stream,//stream_datos, //nombre == '' ? firestoreInstance.collection('Producto').snapshots() : firestoreInstance.collection('Producto').where('nombre', isEqualTo: nombre).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return new Center(child: new CircularProgressIndicator());
}
return
Stack(children: [
Column(children:[
Container(
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(1.0),
border: Border.all(
color: Colors.grey.withOpacity(0.5), width: 1.0),
color: Color.fromARGB(255, 255, 255, 255)),
child: Row(
children: [
IconButton(
(...)
),
onPressed: () {
showDialog(
context: context,
builder: (_) =>mostrar_filtros(filtro_alergenos)
);
},
),
Expanded(
(...)
),
IconButton(
(...)
),
onPressed: () {
setState(() {
nombre = nombre_filtro.text;
if(nombre==''){
controller.stream.drain();
setState(() {
controller.addStream(stream_datos);
});
//stream_datos = firestoreInstance.collection('Producto').where('nombre', isEqualTo: nombre).snapshots();
}else{
stream_datos = firestoreInstance.collection('Producto').where('nombre', isEqualTo: nombre).snapshots();
controller.stream.drain();
controller.addStream(stream_datos);
setState(() {
print("ey");
});
//stream_datos = firestoreInstance.collection('Producto').snapshots();
}
});
},
),
(...)
CodePudding user response:
You can do that by not using the StreamController and instead hold a reference to the Stream while you do your filter, such as:
Stream? filterStream;
String? filterValue;
// you can create a method that performs the filtering for you, such as:
void resetStreamWithNameFilter() {
setState(() {
// return all products if your filter is empty
if (filter.isEmpty) {
filterStream = FirebaseFirestore.instance.collection('Producto').snapshots();
return;
}
// if the filter is not empty, then filter by that field 'nombre'
valueStream = FirebaseFirestore.instance.collection('team').where('nombre', isEqualTo: filter).snapshots();
});
}
Then in your build method, this would be the first method you call; also use the newly created filterStream field, as in:
@override
Widget build(BuildContext context) {
// call it initially, to reset the stream
resetStreamWithNameFilter();
return StreamBuilder(
stream: filterStream,
builder: (context, snapshot) {
// do what you want to do here, but trigger the filter accordingly
List<QueryDocumentSnapshot> docs = (snapshot.data as QuerySnapshot).docs;
return Column(
children: [
Text(docs.length.toString()),
TextButton(
onPressed: () {
// for example, filter by all products named 'camisa'
filterString = 'camisa';
// then, execute the resetStreamWithNameFilter
resetStreamWithNameFilter();
}
)
]
);
}
);
}
I created a Gist so you can get the idea. Run it on DartPad and check it out.