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











up vote
0
down vote

favorite












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




























    up vote
    0
    down vote

    favorite












    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


























      up vote
      0
      down vote

      favorite









      up vote
      0
      down vote

      favorite











      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 at 17:26

























      asked Nov 21 at 15:25









      fitu

      3518




      3518
























          1 Answer
          1






          active

          oldest

          votes

















          up vote
          0
          down vote













          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',
            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








            up vote
            0
            down vote













            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

























              up vote
              0
              down vote













              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























                up vote
                0
                down vote










                up vote
                0
                down vote









                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 2 days ago









                fitu

                3518




                3518






























                     

                    draft saved


                    draft discarded



















































                     


                    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







                    Popular posts from this blog

                    A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

                    Calculate evaluation metrics using cross_val_predict sklearn

                    Insert data from modal to MySQL (multiple modal on website)