Why this BlocBuilder do not rebuild my list after second trigger?

120

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];
Share:
120
Dawid Twin
Author by

Dawid Twin

Updated on January 03, 2023

Comments

  • Dawid Twin
    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
      Salih Can about 2 years
      Can you add "MovieState" detail, please?
    • CLucera
      CLucera about 2 years
      Usually, 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
      Dawid Twin about 2 years
      @CLucera Thank you man, I forgot to add parameters to Equatable props, thank you very much!