Can't retrieve a Stream List of map from cloud firestore for flutter using Getx Statemanagement [Updated]

Issue

I’m trying to retrieve a stream List of NewsModel from cloud firestore but ultimately failing. Codes and images are as follows.

I have a collection ‘news’ > document ‘USA’ > field list. // list is an array which contains list of map. I want to retrieve this list.

How can I retrieve data from firebase, store in the model object and use the model for UI?

Error:

The following NoSuchMethodError was thrown building:
The method '[]' was called on null.
Receiver: null
Tried calling: [](0)

Code are as follow:

Model:

class NewsModel {
  String title, titleImage, brief;
  List aList;

  NewsModel({this.title, this.titleImage, this.brief, this.aList});

  factory NewsModel.fromMap(dynamic fieldData) {
    return NewsModel(
        title: fieldData['title'],
        titleImage: fieldData['titleImage'],
        brief: fieldData['brief'],
        aList: fieldData['mediaDescList']);
  }
}

Controller:

class Controller extends GetxController {
  // onInit
  @override
  void onInit() {
    finalNewsModel.bindStream(streamDemo());
    fetchDocs();
    super.onInit();
  }

  //
  Rxn<List<NewsModel>> finalNewsModel = Rxn<List<NewsModel>>();

  //
  List<NewsModel> get newsModelList => finalNewsModel.value;

  //
  Stream<List<NewsModel>> streamDemo() {
    return FirebaseFirestore.instance
        .collection('news')
        .doc('USA')
        .snapshots()
        .map((ds) {
      var mapData = ds.data();
      List mapList = mapData['list'];
      List<NewsModel> newsModelList = [];
      mapList.forEach((element) {
        newsModelList.add(NewsModel.fromMap(element));
      });
      return newsModelList;
    });
  }
}

UI:

class HomeBody extends StatefulWidget {
  @override
  _HomeBodyState createState() => _HomeBodyState();
}

class _HomeBodyState extends State<HomeBody> {

//
  final _controller = Get.put(Controller());

  //
  NewsModel _newsModel = NewsModel();

     @override
  Widget build(BuildContext context) {
    return Container(
      child: GetBuilder<Controller>(builder: (_controller) {
        if (_controller.newsModelList == null) {
          return Text('Loading');
        } else if (_controller.newsModelList.isEmpty) {
          return Text('Empty List');
        } else {
          return ListView.builder(
            itemCount: _controller.newsModelList.length,
            itemBuilder: (context, index) {
              return MyContainer(
                title: _newsModel.title[index],
                titleImage: _newsModel.titleImage[index],
              );
            },
          );
        }
      }),
    );
  }
}

enter image description here

Solution

Error 1:

The getter ‘length’ was called on null.

The error is coming from your null check below:

    if (_controller.newsModelList.length == null) {
      return Text('Loading');
    }

You’re checking if the length of the list is null but the list itself is null so you’re calling .length on null.
That’s the reason the error says:

The getter ‘length’ was called on null.

Solution:

You should rather check if the list itself is null like below:

    if (_controller.newsModelList == null) {
      return Text('Loading');
    }

Now if you want to check if the list is empty, you have to first ensure the list is not null, then you can call .isEmpty() on the list.

You can then add your existing else statement to show the list of MyContainer widgets.

Error 2:

The following NoSuchMethodError was thrown building:
The method ‘[]’ was called on null.
Receiver: null
Tried calling:

This error is because you’re trying to get a value of a null object.
And it occurs in the lines below:

    title: _newsModel.title[index],
    titleImage: _newsModel.titleImage[index],

This is because _newsModel.title and _newsModel.titleImage is null since you created the _newsModel object without supplying the parameters.

Solution:

The solution will be to delete this line below from your build method:

      NewsModel _newsModel = NewsModel();

Then you can get your _newModel variable from the _controller.newsModelList list.

Your new UI code containing both solutions should be updated to the code block below:

    class HomeBody extends StatefulWidget {
      @override
      _HomeBodyState createState() => _HomeBodyState();
    }
    
    class _HomeBodyState extends State<HomeBody> {
    
    //
      final _controller = Get.put(Controller());
    
         @override
      Widget build(BuildContext context) {
        return Container(
          child: Obx(() {
        if (_controller.newsModelList == null) {
          return Text('Loading');
        } else if (_controller.newsModelList.isEmpty) {
          return Text('Empty List');
        } else {
          return ListView.builder(
            itemCount: _controller.newsModelList.length,
            itemBuilder: (context, index) {
              final NewsModel _newsModel = _controller.newsModelList[index];
              return MyContainer(
                title: _newsModel.title,
                titleImage: _newsModel.titleImage,
                index: index,
              );
            },
          );
        }
      }),
        );
      }

Answered By – Victor Eronmosele

Answer Checked By – Marie Seifert (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.