How to retrieve list of data collection from fire store using bloc

Viewed 59

I am trying to retrieve list of collection of data from fire store as the the below one:

enter image description here

and this is my below States Class:

abstract class AppStates {}

class AppInitialState extends AppStates {}

class AppGetCategoriesLoadingState extends AppStates {}

class AppGetCategoriesSuccessState extends AppStates {}

class AppGetCategoriesErrorState extends AppStates {
  final String error;

  AppGetCategoriesErrorState(this.error);
}

And this is the below cubit class I have:

class AppCubit extends Cubit<AppStates> {
  AppCubit() : super(AppInitialState());

  static AppCubit get(context) => BlocProvider.of(context);

  List<CategoryModel> categoryModel;

  final CollectionReference _categoryCollectionRef =
      FirebaseFirestore.instance.collection('PetsCategories');

  Future<List<QueryDocumentSnapshot>> getCategory() async {
    var value = await _categoryCollectionRef.get();

    return value.docs;
  }

  void getCategoriesData() {
    emit(AppGetCategoriesLoadingState());
    getCategory().then((value) {
      // print(value.data());
      for (int i = 0; i < value.length; i++) {
        categoryModel.add(CategoryModel.fromJson(value[i].data()));
      }
      emit(
        AppGetCategoriesSuccessState(),
      );
    }).catchError((error) {
      print(error.toString());
      emit(
        AppGetCategoriesErrorState(
          error.toString(),
        ),
      );
    });
  }
}

and this is the below Screen I have:

return BlocConsumer<AppCubit, AppStates>(
      listener: (context, state) {},
      builder: (context, state) {
        var cubit = AppCubit.get(context);
        return Column(
          children: [
            SizedBox(
              height: 30,
            ),
            Padding(
              padding: const EdgeInsets.only(left: 20.0),
              child: CustomText(
                text: "Categories",
              ),
            ),
            SizedBox(
              height: 30,
            ),
            Container(
              height: 100,
              child: ListView.separated(
                shrinkWrap: true,
                physics: BouncingScrollPhysics(),
                itemCount: cubit.categoryModel.length,
                scrollDirection: Axis.horizontal,
                itemBuilder: (context, index) {
                  return Column(
                    children: [
                      Container(
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(50),
                          color: Colors.grey.shade100,
                        ),
                        height: 60,
                        width: 60,
                        child: Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: Image.network(cubit.categoryModel[index].image),
                        ),
                      ),
                      // SizedBox(
                      //   height: 20,
                      // ),
                      CustomText(
                        text: cubit.categoryModel[index].name,
                      ),
                    ],
                  );
                },
                separatorBuilder: (context, index) => SizedBox(
                  width: 20,
                ),
              ),
            ),
          ],
        );
      },
    );

and this is the error I have:

The following NoSuchMethodError was thrown building BlocBuilder<AppCubit, AppStates>(dirty, state: _BlocBuilderBaseState<AppCubit, AppStates>#fe322):
The getter 'length' was called on null.
Receiver: null
Tried calling: length

The relevant error-causing widget was: 
  BlocConsumer<AppCubit, AppStates> file:///Users/mahmoudalharoon/Desktop/IPetApp/IPet%20FlutterProject/ipet/lib/modules/discover/discover_screen.dart:17:12
When the exception was thrown, this was the stack: 
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1      DiscoverScreen.build.<anonymous closure> (package:ipet/modules/discover/discover_screen.dart:40:48)
#2      BlocBuilder.build (package:flutter_bloc/src/bloc_builder.dart:90:57)
#3      _BlocBuilderBaseState.build (package:flutter_bloc/src/bloc_builder.dart:151:21)
#4      StatefulElement.build (package:flutter/src/widgets/framework.dart:4612:27)
...
2 Answers

You are emitting your loading state which will trigger the BlocConsumer builder. To avoid this error, you'll need to check the state of the cubit to make sure that it is AppGetCategoriesSuccessState. This way the cubit will have defined the categoryModel variable and will be able to be used.

I would recommend to avoid class variables within your Cubit. It would be better to emit the state with the values that you want to display.

class AppGetCategoriesSuccessState extends AppStates {
   const AppGetCategoriesSuccessState({required this.values});
   final List<CategoryModel> values;
}

then in your bloc consumer


// within the BlocConsumer builder
if (state is AppGetCategoriesSuccessState){
   final _values = state.values;
   // use your values here
}

This is the full right code I figured...

Here's the CategoryModel Class:

class CategoryModel {
  String image;
  String type;

  CategoryModel({
    this.image,
    this.type,
  });

  CategoryModel.fromJson(Map<String, dynamic> map) {
    // if (map == null) {
    //   return;
    // }
    image = map['image'];
    type = map['type'];
  }

  Map<String, dynamic> toMap() {
    return {
      'type': type,
      'image': image,
    };
  }
}

this is the method which is in the cubit class:

  void getCategoriesData() {
    FirebaseFirestore.instance.collection('PetsCategories').get().then((value) {
      value.docs.forEach((element) {
        categories.add(CategoryModel.fromJson(element.data()));
      });
      emit(AppGetCategoriesSuccessState());
    }).catchError((error) {
      emit(AppGetCategoriesErrorState(error.toString()));
    });
  }

this is the below Screen:

class DiscoverScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocConsumer<AppCubit, AppStates>(
      listener: (context, state) {},
      builder: (context, state) {
        return Column(
          children: [
            SizedBox(
              height: 30,
            ),
            Padding(
              padding: const EdgeInsets.only(left: 20.0),
              child: CustomText(
                text: "Categories",
              ),
            ),
            SizedBox(
              height: 30,
            ),
            buildCategoryItem(context),
          ],
        );
      },
    );
  }

  Widget buildCategoryItem(context) {
    return Container(
      height: 100,
      child: ListView.separated(
        shrinkWrap: true,
        physics: BouncingScrollPhysics(),
        itemCount: AppCubit.get(context).categories.length,
        scrollDirection: Axis.horizontal,
        itemBuilder: (context, index) {
          return Column(
            children: [
              Container(
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(50),
                  color: Colors.grey.shade100,
                ),
                height: 60,
                width: 60,
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Image.network(
                      AppCubit.get(context).categories[index].image),
                ),
              ),
              // SizedBox(
              //   height: 20,
              // ),
              CustomText(
                text: AppCubit.get(context).categories[index].type,
              ),
            ],
          );
        },
        separatorBuilder: (context, index) => SizedBox(
          width: 20,
        ),
      ),
    );
  }
}
Related