Why this BlocBuilder do not rebuild my list after second trigger?
BLoC new state is only emitted when oldState != newState
this means that we need a way to correctly differentiate between two instances of the state, we have two ways here, or by overriding the equals operator of your state, or like in your case we may use Equatable
to have an easy way to check for equality.
class MovieState extends Equatable {
const MovieState({
this.status = MovieStatus.initial,
this.movies = const <Movie>[],
});
final MovieStatus status;
final List<Movie> movies;
MovieState copyWith({
MovieStatus? status,
List<Movie>? posts,
}) {
return MovieState(
status: status ?? this.status,
movies: posts ?? this.movies,
);
}
@override
List<Object?> get props => []; <--- Missing props
}
Your class however is missing the props, so any instance of MovieState will be equal! and the state will be emitted only once, the easy fix here is to add the property to your props methods in order to have a proper check
@override
List<Object?> get props => [status, movies];
Dawid Twin
Updated on January 03, 2023Comments
-
Dawid Twin over 1 year
I have cubit in which I download movies and pass it to the list:
class MovieSearchCubit extends Cubit<MovieState> { MovieSearchRepoInterface movieSearchRepository; List<Movie> emptyList = []; MovieSearchCubit({required this.movieSearchRepository}) : super(const MovieState()); Future<void> fetchMovies(String movieName) async { if (state.status != MovieStatus.failure) { final movies = await movieSearchRepository.searchMovies(movieName); if (movies.isRight()) { emit(state.copyWith( status: MovieStatus.success, posts: List.of(state.movies) ..addAll( movies.getOrElse((l) => emptyList), ))); } else { emit(state.copyWith(status: MovieStatus.failure)); } } } }
and I'm calling it in BlocBuilder like this:
BlocBuilder<MovieSearchCubit, MovieState>( builder: (context, state) { switch (state.status) { case MovieStatus.failure: return const Center(child: Text('failed to fetch posts')); case MovieStatus.success: if (state.movies.isEmpty) { return const Center(child: Text('no posts')); } return Expanded( child: ListView.builder( shrinkWrap: true, itemBuilder: (BuildContext context, int index) { return index >= state.movies.length ? BottomLoader() : MovieCard( }); }, itemCount: state.movies.length, ), ); default: return Container(); } }),
I'm triggering cubit function fetchMovies by pressing button:
IconButton( icon: const Icon(Icons.search), onPressed: () async { await context.read<MovieSearchCubit>().fetchMovies(inputStr); }, ),
But the list is build only the first time, data is gathered correctly, the problem is with displaying it. Thanks for any tips.
Added later: Here how looks like MovieState
class MovieState extends Equatable { const MovieState({ this.status = MovieStatus.initial, this.movies = const <Movie>[], }); final MovieStatus status; final List<Movie> movies; MovieState copyWith({ MovieStatus? status, List<Movie>? posts, }) { return MovieState( status: status ?? this.status, movies: posts ?? this.movies, ); } @override List<Object?> get props => []; }
-
Salih Can about 2 yearsCan you add "MovieState" detail, please?
-
CLucera about 2 yearsUsually, this is caused by the equality check of the State, BLoC won't emit two times the same state, so if for some reason the equality check is true it won't be sent twice, as @SalihCan suggested you can share the MovieState code to better understand, for example, are you using Equatable and maybe some prop is missing?
-
Dawid Twin about 2 years@CLucera Thank you man, I forgot to add parameters to Equatable props, thank you very much!
-