Home > Mobile >  Flutter - BloC Cubit function doesn't emitting state
Flutter - BloC Cubit function doesn't emitting state

Time:12-20

I'm creating a Flutter application. I'm added a BloC to my project for management state. I created a list with data. And I want to add item to ListView manually with button 'Add'.

I'm wrote a code:

My Item Cubit

class ItemCubit extends Cubit<List<Item>> {
  ItemCubit() : super([]);

  void addItem(item){
    state.add(item);
    emit(state);
  }
}

Page of Items with Provider:

class SearchPage extends StatelessWidget {
  const SearchPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider(
        create: (_) => ItemCubit(),
        child: Search(),
      ),
    );
  }
}

And I call the BlocBuilder in Stateless Widget like this:

body: BlocBuilder<MarketCubit, List<Market>>(
            builder: (context, items) => TabBarView(...))

So when I call my function from state:

Item item = Item(1, 'Item 1');


ElevatedButton(onPressed:(){
  context.read<ItemCubit>().addItem(item);
 }, child: Text('Add Item')),

The ListView doesn't updating. What's problem? Thanks a lot!

CodePudding user response:

The problem is that you are just adding the item to the state and re-emitting the same (modified) list. Bloc will essentially check the value of the state object to see if it is a new object when you call emit(), if it is the 'same list' as the last state, it will do nothing. It is confusing because even though you are adding a new value to the list, it is still the same object in a sense, just with a new value.

So there should be an easy fix if you just create a new list instead of modifying the original one and emit that.

For example, this should fix the issue:

emit([...state, newValue])

or

final newList = List.from(state)..add(newValue);
emit(newList);

(I had trouble finding in the bloc documentation where this is explained, but there are a number of issue threads where it is discussed -https://github.com/felangel/bloc/issues/2374)

Essentially, the state should be 'immutable', so if you are going to emit a new state, you should actually create a new instance of whatever you are going to emit. The way I typically use the state is to have a class with final fields for whatever state the cubit is tracking, and a copyWith method that makes it easy to create a new instance with any modified fields:

YourCubitState{
    final List stateList;
    final otherField;

    const YourCubitState({
        this.stateList = [], 
        this.otherField,
    }); 

    YourCubitState copyWith({
        List? stateList,
        otherField,
    }) => 
        YourCubitState(
           stateList: stateList ?? this.stateList,
           otherField: otherField ?? this.otherField,
        );   

}

Maybe that is unnecessary if the state is just a single list, but if you find yourself wanting to add properties to the state, that is a good pattern to follow.

CodePudding user response:

ItemCubit class superclass not holding your state. You should manage yourself states. So, what I say, I'm saying create list variable inside ItemCubit class and every call addItem method add item to this list and emit this list.

Your Cubit class should be as below;

abstract class ItemState {
  const ItemState();
}
class ItemInitial extends ItemState {
  const ItemInitial();
}
class ItemStateUpdatedList extends ItemState {
  const ItemStateUpdatedList({required this.list});

  final List<Item> list;
}
class ItemCubit extends Cubit<ItemState> {
  ItemCubit() : super(const ItemInitial());
  
  final List<Item> myItemList = [];

  void addItem(item){
    myItemList.add(item);
    emit(ItemStateUpdatedList(list: myItemList));
  }
}
body: BlocBuilder<ItemCubit, ItemState>(
          builder:(context, state) {
            if(state is ItemStateUpdatedList){
              final list = state.list;
              return TabBarView(...);
            }
            return const SizedBox.shrink();
          },
)
  • Related