Flutter: Using 2 nested StreamBuilders where one of them is not working as expected
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
add a comment |
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
add a comment |
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
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
stream flutter
edited Nov 21 '18 at 17:26
fitu
asked Nov 21 '18 at 15:25
fitufitu
3518
3518
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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?
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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?
add a comment |
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?
add a comment |
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?
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?
answered Nov 26 '18 at 16:10
fitufitu
3518
3518
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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