List of object dosen't work correctly
MajdSallora opened this issue Β· 12 comments
Bloc with equatable
I have List and change one property of in the list
then emiting in bloc state not update
if i removed equatable from state class it work
ex:
class Posts extends Equatable{
final String id;
final String name;
final bool isLiked;
Posts ({
requierd this.id,
requierd this.name,
requierd this.isLiked,
});
@override
List<Object?> get props => [id,name,isLiked];
}
class PostState extends Equatable {
final List<Posts> posts;
const PostState({
this.posts = const [],
});
PostState copyWith({List<Posts>? posts}) {
PostState(posts: posts ?? this.posts,);
}
@override
List<Object?> get props => [posts];
}
makePostLiked(MakePostLiked event, Emitter emit){
var list = state.posts;
var itemToEdit = state.posts[event.index];
itemToEdit.isLike = true;
list.removeAt(event.index);
list.insert(event.index,itemToEdit);
emit(
state.copyWith(posts:list)
);
}
what i else tried
emit(
state.copyWith(posts:[...list])
);
emit(
state.copyWith(posts: List.of(list))
);
equatable: ^2.0.5
flutter_bloc: ^8.1.1
flutter_sdkVersion : 3.7.1
any solution?
I have the same issue when trying to emit new state after updating item in list
Hello, your Posts
also need to extend Equatable
Hi @MajdSallora π
Can you please provide a link to a minimal reproduction sample? As @yshean pointed out, a class which extends Equatable
should also have all its properties also extend Equatable
in order to equality comparisons to work properly.
Hi @MajdSallora π Can you please provide a link to a minimal reproduction sample? As @yshean pointed out, a class which extends
Equatable
should also have all its properties also extendEquatable
in order to equality comparisons to work properly.
I make class Posts extands with equatable but same thing when emit in bloc
if do like this its work
emit(state.copyWith(posts:[]) );
emit(state.copyWith(posts:list) );
but it will build twice!
Hello, your
Posts
also need to extendEquatable
in example the class Posts extend with Equatable but same thing
class PostState extends Equatable {
final List<Posts> posts;
final int timestamp;
const PostState({
this.posts = const [],
this.timestamp = 0,
});
PostState copyWith({List<Posts>? posts, int? timestamp}) {
PostState(posts: posts ?? this.posts, timestamp: timestamp ?? 0);
}
@override
List<Object?> get props => [posts, timestamp];
}
@MajdSallora The same bug occurred, so I added a timestamp property to handle the hash value changing.
@felangel I hope that bug will be fixed.
I'm not able to help without a minimal reproduction sample. Can anyone who is still experiencing this issue please provide a minimal reproduction sample that clearly illustrates the issue? Thanks!
So because you are working with a list of posts, it gets a little trickier. The way Bloc works is that it compares the old state with the new state to figure out whether it needs to build again. By default, dart checks for instances to see if 2 classes are equal, hence why it works if you remove Equatable. (You create a new instance when you use copyWith
and then you emit)
List<Object?> get props => posts;
Now, another issue is that you're modifying the posts directly. You should never modify any field in the state, instead you should create a copy of the state and replace it. For example, if you want to add a new post do the following:
// working
void addPost(Post post) {
/// create a copy of the current list
final list = state.posts.toList();
/// modify it however you want
list.add(post);
/// add it to the new state
emit(state.copyWith(posts: list));
/// this works because you are basically replacing the old list with a
/// completely new one, not modifying the old list
}
// not working
void addPostTheWrongWay(Post post) {
/// use the reference to the list
final list = state.posts;
/// modify it however you want
list.add(post);
/// add it to the new state
emit(state.copyWith(posts: list));
/// this doesn't work because you are modifying the old list.
/// When Bloc then checks for changes, it will see that the old list and the
/// "new" list (it's actually the old list) are equal
}
The state file:
class PostState extends Equatable {
final List<Post> posts;
const PostState({
this.posts = const [],
});
PostState copyWith({List<Post>? posts}) {
return PostState(
posts: posts ?? this.posts,
);
}
@override
/// posts is made up of elements that extend Equatable
List<Object?> get props => [posts];
}
class Post extends Equatable {
final String id;
final String name;
final bool isLiked;
const Post({
required this.id,
required this.name,
required this.isLiked,
});
@override
List<Object?> get props => [id, name, isLiked];
}
If you want to like a post:
void likePost(int index) {
final posts = state.posts.toList();
/// this is when something like copyWith comes in handy
posts[index] = Post(
id: posts[index].id,
name: posts[index].name,
isLiked: true,
);
emit(state.copyWith(posts: posts));
}
By the way, toList
creates a copy of the list.
You can try this
@override
bool operator ==(Object other) => identical(
posts.hashCode,
other.hashCode,
);
I usually do this, when i need to update List in an instance of state.
var lastPostsState = state.posts;
emit(state.copyWith(posts : [...lastPostsState],);
Closing this since there arenβt any actionable steps I can take without a minimal reproduction sample.