cubit state return always a null value

Issue

i am learning flutter-bloc with freezed, i am trying to retrieve a data from sqflite. i have a achieved that without bloc pattern, it worked fine. but when i use bloc to do so the return value is always null!.

This Button when is clicked it is supposed to retrieve data:

  BlocBuilder<DisplayNoteCubit, DisplayNoteState>(
                      builder: (context, state) {
                        return MaterialButton(
                          onPressed: () async {
                            print( '\n print all Data \n ${state.maybeMap(orElse: () {}, getData: (g) => g.notes)}');
                          },
                          child: const Text("Click Me"),
                        );
                      },
                    ),

bloc code is:

final DBHelper _db=DBHelper.instance;

class DisplayNoteCubit extends Cubit<DisplayNoteState> {
  DisplayNoteCubit() : super(const DisplayNoteState.initial());
  void  getData() {
    state.maybeMap(
        orElse: () {},
        getData: (getData) async{
          emit(getData.copyWith(notes:await  _db.getAllNotesFormDB()));
        });
  }
}

state code is:

@freezed
abstract class DisplayNoteState with _$DisplayNoteState {
  const factory DisplayNoteState.initial() = Initial;
  const factory DisplayNoteState.getData({required List<Map<String, dynamic>> notes}) = GetData;
}

Solution

When using Cubit, your state lifecycle looks like this:

  1. You call a method on Cubit;
  2. Cubit processes the method and emits a new state;
  3. Your UI subscribes to the state and re-renders accordingly.

Now, you need to fix several aspects in your code:

Step 1. BlocBuilder is used only when you need to subscribe to state changes and render UI based on the current state. In your case, you only want to execute the method with your button, so BlocBuilder is not needed in this case.

Change this:

BlocBuilder<DisplayNoteCubit, DisplayNoteState>(
  builder: (context, state) {
    return MaterialButton(
      onPressed: () async {
        print( '\n print all Data \n ${state.maybeMap(orElse: () {}, getData: (g) => g.notes)}');
      },
      child: const Text("Click Me"),
    );
  },
),

to this:

return MaterialButton(
  onPressed: () async {
    context.read<DisplayNoteCubit>().getData(); // Triggers getData() method on Cubit
  },
  child: const Text("Click Me"),
);

Step 2. Retrieve data and emit state change.

Change this:

final DBHelper _db=DBHelper.instance;

class DisplayNoteCubit extends Cubit<DisplayNoteState> {
  DisplayNoteCubit() : super(const DisplayNoteState.initial());
  void  getData() {
    state.maybeMap(
        orElse: () {},
        getData: (getData) async{
          emit(getData.copyWith(notes:await  _db.getAllNotesFormDB()));
        });
  }
}

to this:

final DBHelper _db=DBHelper.instance;

class DisplayNoteCubit extends Cubit<DisplayNoteState> {
  DisplayNoteCubit() : super(const DisplayNoteState.initial());

  Future<void> getData() async {
    final notes = await _db.getAllNotesFormDB();

    emit(DisplayNoteState.getData(notes: notes)); // Emits state with retrieved notes
  }
}

Step 3. Render your UI. Now, you should use the BlocBuilder method to render your state. You could use state.maybeMap() here as you tried before. For example:

BlocBuilder<DisplayNoteCubit, DisplayNoteState>(
  builder: (context, state) {
     return state.maybeMap(
       getData: (getDataState) { ... },
       orElse: () { ... },
     );
  },
),

Answered By – mkobuolys

Answer Checked By – Cary Denson (FlutterFixes Admin)

Leave a Reply

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