Flutter BlocBuillder not receiving state updates

Issue

bloc newbie here.
As per title, i have a BlocBuilder which is a child of a BlocProvider.

This BlocBuilder is not receiving updates after the very first one and i can’t understand why after many hours. Can someone explain that to me?

The goal is to display an horizontal list of Cards (ICards are just customized cards) and below that a list of page indicator which change colour based on the received state.


  @override
  Widget build(BuildContext context) {
    return Scaffold(
        [...]
        child: BlocProvider(
          create: (context) => CarouselCubit(),
          child: Column(
            children: <Widget>[
              const ICardCarousel(),
              [...]
class ICardCarousel extends StatelessWidget {
  const ICardCarousel({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CarouselCubit, CarouselState>(
      builder: (ctx, state) {
        print(
            'received state $state, with page selected ${state.pageSelected}');
        return Column(
          children: <Widget>[
            _cardList(ctx),
            _pageIndicatorContainer(state.pageSelected),
          ],
        );
      },
    );
  }

  Widget _cardList(BuildContext context) {
    return SizedBox(
      height: 170,
      child: PageView.builder(
        scrollDirection: Axis.horizontal,
        itemBuilder: (context, i) {
          return _viewHolder();
        },
        itemCount: 3,
        onPageChanged: (pageNumber) {
          print('onPageChanged $pageNumber');
          BlocProvider.of<CarouselCubit>(context).onPageChanged(pageNumber);
        },
      ),
    );
  }

  Widget _pageIndicatorContainer(int pageSelected) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: _buildPageIndicator(pageSelected),
    );
  }

  Widget _viewHolder() {
    return Align(
      alignment: Alignment.center,
      child: Wrap(
        children: const <Widget>[
          ICard(
            width: 200,
            height: 100,
            logo: 'assets/logo.png',
            qrCode: 'assets/qr-code.png',
          ),
        ],
      ),
    );
  }

  Widget _pageIndicator(bool isActive) {
    return SizedBox(
      height: 10,
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 150),
        margin: const EdgeInsets.symmetric(horizontal: 4.0),
        height: isActive ? 10 : 8.0,
        width: isActive ? 12 : 8.0,
        decoration: BoxDecoration(
          boxShadow: [
            isActive
                ? BoxShadow(
                    color: Colors.lightBlue.withOpacity(0.72),
                    blurRadius: 4.0,
                    spreadRadius: 1.0,
                    offset: const Offset(
                      0.0,
                      0.0,
                    ),
                  )
                : const BoxShadow(
                    color: Colors.transparent,
                  )
          ],
          shape: BoxShape.circle,
          color: isActive ? Colors.lightBlue : Colors.black,
        ),
      ),
    );
  }

  List<Widget> _buildPageIndicator(int selectedindex) {
    print('buildPageIndicator: selectedindex $selectedindex');
    List<Widget> list = [];
    for (int i = 0; i < 3; i++) {
      list.add(
          i == selectedindex ? _pageIndicator(true) : _pageIndicator(false));
    }
    return list;
  }
}

class CarouselState extends Equatable {
  CarouselState({required this.pageSelected}) {
    print('creating new CarouselState. pageSelected: $pageSelected');
  }

  @override
  List<Object> get props => [];

  final int pageSelected;
}

class CarouselCubit extends Cubit<CarouselState> {
  CarouselCubit() : super(CarouselState(pageSelected: 0));

  void onPageChanged(int page) {
    emit(CarouselState(pageSelected: page));
  }
}

Prints:

Restarted application in 1.183ms.
I/flutter ( 6845): creating new CarouselState. pageSelected: 0
I/flutter ( 6845): received state CarouselState(), with page selected 0
I/flutter ( 6845): buildPageIndicator: selectedindex 0
I/flutter ( 6845): onPageChanged 1
I/flutter ( 6845): creating new CarouselState. pageSelected: 1
I/flutter ( 6845): received state CarouselState(), with page selected 1
I/flutter ( 6845): buildPageIndicator: selectedindex 1
I/flutter ( 6845): onPageChanged 2
I/flutter ( 6845): creating new CarouselState. pageSelected: 2

Solution

You have to edit your CarouselState to include the pageSelected inside the props as below :

class CarouselState extends Equatable {
  CarouselState({required this.pageSelected}) {
    print('creating new CarouselState. pageSelected: $pageSelected');
  }

  @override
  List<Object> get props => [pageSelected];

  final int pageSelected;
}

The reason is the BLoC library prevents re-emission of identical states without another state preceding the re-emission, and if you don’t include the param, the equatable cannot function correctly

Answered By – TheMisplacedMechEngineer

Answer Checked By – Pedro (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *