Home > Blockchain >  Change a stream in Streambuilder dynamically
Change a stream in Streambuilder dynamically

Time:03-18

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.

  • Related