Flutter: Using 2 nested StreamBuilders where one of them is not working as expected












0















I'm building and App where I'm using 2 StreamBuilders (one inside another).
The outer one consumes an Stream<List<User>> and render that list.
The inner one consumes Stream<User> where I can check if the user is favorite or not.



Here is the code:



users_page.dart



@override
Widget build(BuildContext context) {
return Scaffold(
child: StreamBuilder<List<User>>(
stream: userBloc.outList,
initialData: ,
builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
final List<User> users = snapshot.data;
return buildList(users);
})
}


Widget buildList(List<User> users) {
return ListView.builder(
itemCount: users.length,
itemBuilder: (BuildContext context, int index) {
final User user = users[index];
return ListTile(
title: Text('${user.firstName}'),
trailing: buildFavoriteButton(user));
});
}

Widget buildFavoriteButton(User user) {
User oldUser = user;
return StreamBuilder<User>(
stream: userBloc.outFavorite,
initialData: oldUser,
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
final User newUser = snapshot.data;
if (oldUser.id == newUser.id) {
oldUser = newUser;
}
return IconButton(
icon: Icon(Icons.favorite, color: oldUser.isFavorite ? Colors.red : Colors.blueGrey),
onPressed: () {
print('onPressed: This is called once');
userBloc.inFavorite.add(newUser);
});
});
}


users_block.dart



class UserBloc {
final Repository _repository = Repository();

// More variables like the BehaviourSubject for outList and so on ...

final BehaviorSubject<User> _userFavoriteSubject = BehaviorSubject<User>();
Stream<User> _outFavorite = Stream.empty();
Stream<User> get outFavorite => _outFavorite;
Sink<User> get inFavorite => _userFavoriteSubject;

UserBloc() {
_outFavorite = _userFavoriteSubject.switchMap<User>((user) {
print('userBloc: This is called N times')
return user.isFavorite ? _repository.removeFromFavorite(user) : _repository.saveAsFavorite(user);
});
}
}


The outer stream is called once and the onPressed method is called once as well (as expected).



But the problem I'm having is when I press the Icon: userBloc prints N times (where N is the number of rows in the list), like I would pressed the Icon N times.



So the log is:



print: onPressed: This is called once
print: userBloc: This is called N times
print: userBloc: This is called N times
...
print: userBloc: This is called N times


In this case the action (pressing the icon) is executed once, but userBloc gets N inputs.



Why this is happening and how can I solve this problem?



Thanks in advance!










share|improve this question





























    0















    I'm building and App where I'm using 2 StreamBuilders (one inside another).
    The outer one consumes an Stream<List<User>> and render that list.
    The inner one consumes Stream<User> where I can check if the user is favorite or not.



    Here is the code:



    users_page.dart



    @override
    Widget build(BuildContext context) {
    return Scaffold(
    child: StreamBuilder<List<User>>(
    stream: userBloc.outList,
    initialData: ,
    builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
    final List<User> users = snapshot.data;
    return buildList(users);
    })
    }


    Widget buildList(List<User> users) {
    return ListView.builder(
    itemCount: users.length,
    itemBuilder: (BuildContext context, int index) {
    final User user = users[index];
    return ListTile(
    title: Text('${user.firstName}'),
    trailing: buildFavoriteButton(user));
    });
    }

    Widget buildFavoriteButton(User user) {
    User oldUser = user;
    return StreamBuilder<User>(
    stream: userBloc.outFavorite,
    initialData: oldUser,
    builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
    final User newUser = snapshot.data;
    if (oldUser.id == newUser.id) {
    oldUser = newUser;
    }
    return IconButton(
    icon: Icon(Icons.favorite, color: oldUser.isFavorite ? Colors.red : Colors.blueGrey),
    onPressed: () {
    print('onPressed: This is called once');
    userBloc.inFavorite.add(newUser);
    });
    });
    }


    users_block.dart



    class UserBloc {
    final Repository _repository = Repository();

    // More variables like the BehaviourSubject for outList and so on ...

    final BehaviorSubject<User> _userFavoriteSubject = BehaviorSubject<User>();
    Stream<User> _outFavorite = Stream.empty();
    Stream<User> get outFavorite => _outFavorite;
    Sink<User> get inFavorite => _userFavoriteSubject;

    UserBloc() {
    _outFavorite = _userFavoriteSubject.switchMap<User>((user) {
    print('userBloc: This is called N times')
    return user.isFavorite ? _repository.removeFromFavorite(user) : _repository.saveAsFavorite(user);
    });
    }
    }


    The outer stream is called once and the onPressed method is called once as well (as expected).



    But the problem I'm having is when I press the Icon: userBloc prints N times (where N is the number of rows in the list), like I would pressed the Icon N times.



    So the log is:



    print: onPressed: This is called once
    print: userBloc: This is called N times
    print: userBloc: This is called N times
    ...
    print: userBloc: This is called N times


    In this case the action (pressing the icon) is executed once, but userBloc gets N inputs.



    Why this is happening and how can I solve this problem?



    Thanks in advance!










    share|improve this question



























      0












      0








      0








      I'm building and App where I'm using 2 StreamBuilders (one inside another).
      The outer one consumes an Stream<List<User>> and render that list.
      The inner one consumes Stream<User> where I can check if the user is favorite or not.



      Here is the code:



      users_page.dart



      @override
      Widget build(BuildContext context) {
      return Scaffold(
      child: StreamBuilder<List<User>>(
      stream: userBloc.outList,
      initialData: ,
      builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
      final List<User> users = snapshot.data;
      return buildList(users);
      })
      }


      Widget buildList(List<User> users) {
      return ListView.builder(
      itemCount: users.length,
      itemBuilder: (BuildContext context, int index) {
      final User user = users[index];
      return ListTile(
      title: Text('${user.firstName}'),
      trailing: buildFavoriteButton(user));
      });
      }

      Widget buildFavoriteButton(User user) {
      User oldUser = user;
      return StreamBuilder<User>(
      stream: userBloc.outFavorite,
      initialData: oldUser,
      builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
      final User newUser = snapshot.data;
      if (oldUser.id == newUser.id) {
      oldUser = newUser;
      }
      return IconButton(
      icon: Icon(Icons.favorite, color: oldUser.isFavorite ? Colors.red : Colors.blueGrey),
      onPressed: () {
      print('onPressed: This is called once');
      userBloc.inFavorite.add(newUser);
      });
      });
      }


      users_block.dart



      class UserBloc {
      final Repository _repository = Repository();

      // More variables like the BehaviourSubject for outList and so on ...

      final BehaviorSubject<User> _userFavoriteSubject = BehaviorSubject<User>();
      Stream<User> _outFavorite = Stream.empty();
      Stream<User> get outFavorite => _outFavorite;
      Sink<User> get inFavorite => _userFavoriteSubject;

      UserBloc() {
      _outFavorite = _userFavoriteSubject.switchMap<User>((user) {
      print('userBloc: This is called N times')
      return user.isFavorite ? _repository.removeFromFavorite(user) : _repository.saveAsFavorite(user);
      });
      }
      }


      The outer stream is called once and the onPressed method is called once as well (as expected).



      But the problem I'm having is when I press the Icon: userBloc prints N times (where N is the number of rows in the list), like I would pressed the Icon N times.



      So the log is:



      print: onPressed: This is called once
      print: userBloc: This is called N times
      print: userBloc: This is called N times
      ...
      print: userBloc: This is called N times


      In this case the action (pressing the icon) is executed once, but userBloc gets N inputs.



      Why this is happening and how can I solve this problem?



      Thanks in advance!










      share|improve this question
















      I'm building and App where I'm using 2 StreamBuilders (one inside another).
      The outer one consumes an Stream<List<User>> and render that list.
      The inner one consumes Stream<User> where I can check if the user is favorite or not.



      Here is the code:



      users_page.dart



      @override
      Widget build(BuildContext context) {
      return Scaffold(
      child: StreamBuilder<List<User>>(
      stream: userBloc.outList,
      initialData: ,
      builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
      final List<User> users = snapshot.data;
      return buildList(users);
      })
      }


      Widget buildList(List<User> users) {
      return ListView.builder(
      itemCount: users.length,
      itemBuilder: (BuildContext context, int index) {
      final User user = users[index];
      return ListTile(
      title: Text('${user.firstName}'),
      trailing: buildFavoriteButton(user));
      });
      }

      Widget buildFavoriteButton(User user) {
      User oldUser = user;
      return StreamBuilder<User>(
      stream: userBloc.outFavorite,
      initialData: oldUser,
      builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
      final User newUser = snapshot.data;
      if (oldUser.id == newUser.id) {
      oldUser = newUser;
      }
      return IconButton(
      icon: Icon(Icons.favorite, color: oldUser.isFavorite ? Colors.red : Colors.blueGrey),
      onPressed: () {
      print('onPressed: This is called once');
      userBloc.inFavorite.add(newUser);
      });
      });
      }


      users_block.dart



      class UserBloc {
      final Repository _repository = Repository();

      // More variables like the BehaviourSubject for outList and so on ...

      final BehaviorSubject<User> _userFavoriteSubject = BehaviorSubject<User>();
      Stream<User> _outFavorite = Stream.empty();
      Stream<User> get outFavorite => _outFavorite;
      Sink<User> get inFavorite => _userFavoriteSubject;

      UserBloc() {
      _outFavorite = _userFavoriteSubject.switchMap<User>((user) {
      print('userBloc: This is called N times')
      return user.isFavorite ? _repository.removeFromFavorite(user) : _repository.saveAsFavorite(user);
      });
      }
      }


      The outer stream is called once and the onPressed method is called once as well (as expected).



      But the problem I'm having is when I press the Icon: userBloc prints N times (where N is the number of rows in the list), like I would pressed the Icon N times.



      So the log is:



      print: onPressed: This is called once
      print: userBloc: This is called N times
      print: userBloc: This is called N times
      ...
      print: userBloc: This is called N times


      In this case the action (pressing the icon) is executed once, but userBloc gets N inputs.



      Why this is happening and how can I solve this problem?



      Thanks in advance!







      stream flutter






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 21 '18 at 17:26







      fitu

















      asked Nov 21 '18 at 15:25









      fitufitu

      3518




      3518
























          1 Answer
          1






          active

          oldest

          votes


















          0














          I made a test where I defined:



          Widget buildBody() {
          return Column(
          children: <Widget>[
          StreamBuilder<int>(
          stream: userBloc.outState,
          initialData: 0,
          builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
          print("Builder 1");
          print("Snapshot 1: " + snapshot.data.toString());
          return (IconButton(
          icon: Icon(Icons.favorite, color: Colors.red),
          onPressed: () {
          print("onPressed 1");
          userBloc.inEvents.add(1);
          }));
          },
          ),
          StreamBuilder<int>(
          stream: userBloc.outState,
          initialData: 0,
          builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
          print("Builder 2");
          print("Snapshot 2: " + snapshot.data.toString());
          return (IconButton(
          icon: Icon(Icons.favorite, color: Colors.red),
          onPressed: () {
          print("onPressed 2");
          userBloc.inEvents.add(2);
          }));
          },
          )
          ],
          );


          And the stream:



          _outState = _userSubject.switchMap<int>(
          (integer) {
          print("Input (sink): " + integer.toString());
          return doSomething(integer);
          },
          );


          When I run this code and click the IconButton 1, this is the output:



          I/flutter ( 3912): Builder 1
          I/flutter ( 3912): Snapshot 1: 0
          I/flutter ( 3912): Builder 2
          I/flutter ( 3912): Snapshot 2: 0
          I/flutter ( 3912): onPressed 1
          I/flutter ( 3912): Input (sink): 1
          I/flutter ( 3912): Input (sink): 1
          I/flutter ( 3912): Builder 1
          I/flutter ( 3912): Snapshot 1: 1
          I/flutter ( 3912): Builder 2
          I/flutter ( 3912): Snapshot 2: 1


          As you can see the print "Input (sink): 1" is shown twice.
          So for any input to the sink the code inside subject is executed n times, depending on the amount of StreamBuilders subscribed to the stream.



          Is this behaviour okay, or is it a bug?



          I know that the builder function should be call twice because any change in the stream is forwarded to all StreamBuilder subscribed, but the code inside the subject should be call twice too?






          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53415296%2fflutter-using-2-nested-streambuilders-where-one-of-them-is-not-working-as-expec%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            I made a test where I defined:



            Widget buildBody() {
            return Column(
            children: <Widget>[
            StreamBuilder<int>(
            stream: userBloc.outState,
            initialData: 0,
            builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
            print("Builder 1");
            print("Snapshot 1: " + snapshot.data.toString());
            return (IconButton(
            icon: Icon(Icons.favorite, color: Colors.red),
            onPressed: () {
            print("onPressed 1");
            userBloc.inEvents.add(1);
            }));
            },
            ),
            StreamBuilder<int>(
            stream: userBloc.outState,
            initialData: 0,
            builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
            print("Builder 2");
            print("Snapshot 2: " + snapshot.data.toString());
            return (IconButton(
            icon: Icon(Icons.favorite, color: Colors.red),
            onPressed: () {
            print("onPressed 2");
            userBloc.inEvents.add(2);
            }));
            },
            )
            ],
            );


            And the stream:



            _outState = _userSubject.switchMap<int>(
            (integer) {
            print("Input (sink): " + integer.toString());
            return doSomething(integer);
            },
            );


            When I run this code and click the IconButton 1, this is the output:



            I/flutter ( 3912): Builder 1
            I/flutter ( 3912): Snapshot 1: 0
            I/flutter ( 3912): Builder 2
            I/flutter ( 3912): Snapshot 2: 0
            I/flutter ( 3912): onPressed 1
            I/flutter ( 3912): Input (sink): 1
            I/flutter ( 3912): Input (sink): 1
            I/flutter ( 3912): Builder 1
            I/flutter ( 3912): Snapshot 1: 1
            I/flutter ( 3912): Builder 2
            I/flutter ( 3912): Snapshot 2: 1


            As you can see the print "Input (sink): 1" is shown twice.
            So for any input to the sink the code inside subject is executed n times, depending on the amount of StreamBuilders subscribed to the stream.



            Is this behaviour okay, or is it a bug?



            I know that the builder function should be call twice because any change in the stream is forwarded to all StreamBuilder subscribed, but the code inside the subject should be call twice too?






            share|improve this answer




























              0














              I made a test where I defined:



              Widget buildBody() {
              return Column(
              children: <Widget>[
              StreamBuilder<int>(
              stream: userBloc.outState,
              initialData: 0,
              builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
              print("Builder 1");
              print("Snapshot 1: " + snapshot.data.toString());
              return (IconButton(
              icon: Icon(Icons.favorite, color: Colors.red),
              onPressed: () {
              print("onPressed 1");
              userBloc.inEvents.add(1);
              }));
              },
              ),
              StreamBuilder<int>(
              stream: userBloc.outState,
              initialData: 0,
              builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
              print("Builder 2");
              print("Snapshot 2: " + snapshot.data.toString());
              return (IconButton(
              icon: Icon(Icons.favorite, color: Colors.red),
              onPressed: () {
              print("onPressed 2");
              userBloc.inEvents.add(2);
              }));
              },
              )
              ],
              );


              And the stream:



              _outState = _userSubject.switchMap<int>(
              (integer) {
              print("Input (sink): " + integer.toString());
              return doSomething(integer);
              },
              );


              When I run this code and click the IconButton 1, this is the output:



              I/flutter ( 3912): Builder 1
              I/flutter ( 3912): Snapshot 1: 0
              I/flutter ( 3912): Builder 2
              I/flutter ( 3912): Snapshot 2: 0
              I/flutter ( 3912): onPressed 1
              I/flutter ( 3912): Input (sink): 1
              I/flutter ( 3912): Input (sink): 1
              I/flutter ( 3912): Builder 1
              I/flutter ( 3912): Snapshot 1: 1
              I/flutter ( 3912): Builder 2
              I/flutter ( 3912): Snapshot 2: 1


              As you can see the print "Input (sink): 1" is shown twice.
              So for any input to the sink the code inside subject is executed n times, depending on the amount of StreamBuilders subscribed to the stream.



              Is this behaviour okay, or is it a bug?



              I know that the builder function should be call twice because any change in the stream is forwarded to all StreamBuilder subscribed, but the code inside the subject should be call twice too?






              share|improve this answer


























                0












                0








                0







                I made a test where I defined:



                Widget buildBody() {
                return Column(
                children: <Widget>[
                StreamBuilder<int>(
                stream: userBloc.outState,
                initialData: 0,
                builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                print("Builder 1");
                print("Snapshot 1: " + snapshot.data.toString());
                return (IconButton(
                icon: Icon(Icons.favorite, color: Colors.red),
                onPressed: () {
                print("onPressed 1");
                userBloc.inEvents.add(1);
                }));
                },
                ),
                StreamBuilder<int>(
                stream: userBloc.outState,
                initialData: 0,
                builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                print("Builder 2");
                print("Snapshot 2: " + snapshot.data.toString());
                return (IconButton(
                icon: Icon(Icons.favorite, color: Colors.red),
                onPressed: () {
                print("onPressed 2");
                userBloc.inEvents.add(2);
                }));
                },
                )
                ],
                );


                And the stream:



                _outState = _userSubject.switchMap<int>(
                (integer) {
                print("Input (sink): " + integer.toString());
                return doSomething(integer);
                },
                );


                When I run this code and click the IconButton 1, this is the output:



                I/flutter ( 3912): Builder 1
                I/flutter ( 3912): Snapshot 1: 0
                I/flutter ( 3912): Builder 2
                I/flutter ( 3912): Snapshot 2: 0
                I/flutter ( 3912): onPressed 1
                I/flutter ( 3912): Input (sink): 1
                I/flutter ( 3912): Input (sink): 1
                I/flutter ( 3912): Builder 1
                I/flutter ( 3912): Snapshot 1: 1
                I/flutter ( 3912): Builder 2
                I/flutter ( 3912): Snapshot 2: 1


                As you can see the print "Input (sink): 1" is shown twice.
                So for any input to the sink the code inside subject is executed n times, depending on the amount of StreamBuilders subscribed to the stream.



                Is this behaviour okay, or is it a bug?



                I know that the builder function should be call twice because any change in the stream is forwarded to all StreamBuilder subscribed, but the code inside the subject should be call twice too?






                share|improve this answer













                I made a test where I defined:



                Widget buildBody() {
                return Column(
                children: <Widget>[
                StreamBuilder<int>(
                stream: userBloc.outState,
                initialData: 0,
                builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                print("Builder 1");
                print("Snapshot 1: " + snapshot.data.toString());
                return (IconButton(
                icon: Icon(Icons.favorite, color: Colors.red),
                onPressed: () {
                print("onPressed 1");
                userBloc.inEvents.add(1);
                }));
                },
                ),
                StreamBuilder<int>(
                stream: userBloc.outState,
                initialData: 0,
                builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                print("Builder 2");
                print("Snapshot 2: " + snapshot.data.toString());
                return (IconButton(
                icon: Icon(Icons.favorite, color: Colors.red),
                onPressed: () {
                print("onPressed 2");
                userBloc.inEvents.add(2);
                }));
                },
                )
                ],
                );


                And the stream:



                _outState = _userSubject.switchMap<int>(
                (integer) {
                print("Input (sink): " + integer.toString());
                return doSomething(integer);
                },
                );


                When I run this code and click the IconButton 1, this is the output:



                I/flutter ( 3912): Builder 1
                I/flutter ( 3912): Snapshot 1: 0
                I/flutter ( 3912): Builder 2
                I/flutter ( 3912): Snapshot 2: 0
                I/flutter ( 3912): onPressed 1
                I/flutter ( 3912): Input (sink): 1
                I/flutter ( 3912): Input (sink): 1
                I/flutter ( 3912): Builder 1
                I/flutter ( 3912): Snapshot 1: 1
                I/flutter ( 3912): Builder 2
                I/flutter ( 3912): Snapshot 2: 1


                As you can see the print "Input (sink): 1" is shown twice.
                So for any input to the sink the code inside subject is executed n times, depending on the amount of StreamBuilders subscribed to the stream.



                Is this behaviour okay, or is it a bug?



                I know that the builder function should be call twice because any change in the stream is forwarded to all StreamBuilder subscribed, but the code inside the subject should be call twice too?







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 26 '18 at 16:10









                fitufitu

                3518




                3518
































                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53415296%2fflutter-using-2-nested-streambuilders-where-one-of-them-is-not-working-as-expec%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    這個網誌中的熱門文章

                    Xamarin.form Move up view when keyboard appear

                    Post-Redirect-Get with Spring WebFlux and Thymeleaf

                    Anylogic : not able to use stopDelay()