10

I am trying to retrieve data from Firebase Realtime Database into a list in Flutter using a model. My list is returning as null when I do this. I have read several other posts about using Firebase with Flutter but have not found a clear answer. Here is my current approach (the object is called a 'need', and I am trying to retrieve and display a list of needs):

Model:

import 'package:firebase_database/firebase_database.dart';

class Need {
  final String id;
  final String imageUrl;
  final String caption;
  final String title;

  Need({
    this.id,
    this.imageUrl,
    this.caption,
    this.title,
  });

  Need.fromSnapshot(DataSnapshot snapshot) :
    id = snapshot.key,
    imageUrl = snapshot.value["imageUrl"],
    caption = snapshot.value["caption"],
    title = snapshot.value["postTitle"];

  toJson() {
    return {
      "imageUrl": imageUrl,
      "caption": caption,
      "title": title,
    };
  }
}

Database service with Firebase query:

import 'package:firebase_database/firebase_database.dart';
import 'package:Given_Flutter/models/need_model.dart';

class DatabaseService {

  static Future<List<Need>> getNeeds() async {
    Query needsSnapshot = await FirebaseDatabase.instance
      .reference()
      .child("needs-posts")
      .orderByKey();

    print(needsSnapshot); // to debug and see if data is returned

    List<Need> needs;

    Map<dynamic, dynamic> values = needsSnapshot.data.value;
    values.forEach((key, values) {
      needs.add(values);
    });

    return needs;
  }
}

ListView:

import 'package:flutter/material.dart';
import 'package:Given_Flutter/models/need_model.dart';
import 'package:Given_Flutter/services/database_service.dart';
import 'package:Given_Flutter/need_view.dart';

class Needs extends StatefulWidget {
  static final String id = 'needs_screen';

  @override
  _NeedsState createState() => _NeedsState();
}

class _NeedsState extends State<Needs> {
  List<Need> _needs = [];

  @override
  void initState() {
    super.initState();
    _setupNeeds();
  }

  _setupNeeds() async {
    List<Need> needs = await DatabaseService.getNeeds();
    setState(() {
      _needs = needs;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: RefreshIndicator(
        onRefresh: () => _setupNeeds(),
        child: ListView.builder(
          itemCount: _needs.length,
          itemBuilder: (BuildContext context, int index) {
            Need need = _needs[index];
            return NeedView(
              need: need,
            );
          }
        )
      )
    );
  }
}

Firebase Realtime Database structure:

Firebase example

What am I doing wrong?

4
  • What does your debug print output? Commented Apr 29, 2020 at 19:08
  • @GrahamD It is showing an error for this line: Map<dynamic, dynamic> values = needsSnapshot.data.value; Error: The getter 'data' isn't defined for the class 'Query'. Commented Apr 29, 2020 at 20:10
  • @GrahamD I have tried removing that line and the following forEach loop to focus just on whether I am getting data back from Firebase. When I do that, the debug log never prints the snapshot data, even though I have a print line for it. I can't tell if it is because that portion is not being executed or if the snapshot is null. Instead the debug log shows an error "The getter 'length' was called on null.", which makes sense because the needs list being returned to the ListView is empty. Commented Apr 29, 2020 at 20:13
  • Try using needsSnapshot.value instead of needsSnapshot.data.value in the code line: Map<dynamic, dynamic> values = needsSnapshot.data.value; I suggest you put a few more print statements in as strategic points to see what you are actually getting. I don't think you can print a snapshot anyway, you will just get instance of snapshot, you have to print its properties eg. .key, .value Commented Apr 30, 2020 at 7:33

6 Answers 6

8

Map<dynamic, dynamic> values = needsSnapshot.data.value; is wrong.

Value is the data, so just use Map<dynamic, dynamic> values = needsSnapshot.value;

Also the object your appending to to the List is not converted..

// wrong
Map<dynamic, dynamic> values = needsSnapshot.data.value;
    values.forEach((key, values) {
      needs.add(values);
    });

// correct
Map<dynamic, dynamic> values = needsSnapshot.data.value;
    values.forEach((key, values) {
      needs.add(Need.fromSnapshot(values));
    });

One more thing, I'm not 100% sure on this, but if I declare my List as null I have problems when adding value to it, so I always declare the as empty lists.. So instead of List<Need> needs; I'd use List<Need> needs = [];

Hope you solved it already but if not this should work. I just went through the same problem.. Cheers

Sign up to request clarification or add additional context in comments.

7 Comments

I managed to get the data based on your solution, however, when I return the list, it is empty (because the return takes place before data is added to the list in the forEach()). Do you know what I can do so that the return won't execute until my forEach() finishes? It also tells me that the await before the query does nothing.
@DanCrisan Hi, the problem should be that you haven't declared you method as async that's why await is not doing anything and it returns an empty List it doesn't await result to populate it and returns the empty declared. Let me know if this solved it. Cheers.
Hi, my method is definitely declared as async, otherwise I think it would have an error when trying to use await. Instead, I just receive a warning saying it doesn't do anything. If I use something like .once() at the end of the query, await works, however, that transforms my query into a DataSnapshot while I need it to be a query.
@DanCrisan please share some code so I can have a look at it and try to spot the problem ;)
I have posted the code here: stackoverflow.com/q/64277935/10544887
|
4

Just a simple example to get users from firebase realtime database and read once then store and generate user list from it

  final List<User> list = [];

  getUsers() async {
    final snapshot = await FirebaseDatabase.instance.ref('users').get();

    final map = snapshot.value as Map<dynamic, dynamic>;

    map.forEach((key, value) {
      final user = User.fromMap(value);

      list.add(user);
    });
  }
class User {

  final String name;
  final String phoneNumber;

  const User({
    required this.name,
    required this.phoneNumber,
  });

  factory User.fromMap(Map<dynamic, dynamic> map) {
    return User(
      name: map['name'] ?? '',
      phoneNumber: map['phoneNumber'] ?? '',
    );
  }
}

Comments

3

Try this

needsSnapshot.once().then((DataSnapshot snapshot){
  print(snapshot.value.entries.length);

  for (var val in snapshot.value.entries){
    needs.add(new Need(val.id, val.imageUrl,val.caption,val.title));
    //print(val.value.toString());
  }
    //print(needs.length);

Comments

0

Note sure if above worked, same can be achieved as below:

List<Need> yourNeeds=[];
await firestore.collection("Need")
        .getDocuments()
        .then((QuerySnapshot snapshot) {
       snapshot.documents.forEach((f) {
            Needs obj= new Need();
       
        obj.field1=f.data['field1'].toString();
        obj.field2=f.data['field2'].toDouble();

        //Adding value to your Need list
        yourNeeds.add(obj);
});

1 Comment

This question is about firebase realtime database.
0
FirebaseDatabase.instance.ref("Need").onValue.listen((event) {

 final data = 
 Map<String, dynamic>.from(event.snapshot.value as Map,);

 data.forEach((key, value) {
     log("$value");
 });
});

1 Comment

Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, can you edit your answer to include an explanation of what you're doing and why you believe it is the best approach?
-1

Create a one model for holding object. Create a method in it like below,

  ModelName.fromJson(Map<String, dynamic> json) {
    property1 = json['property1'];
    property2 = json['property2'];
  }

Use the below method for converting firebase data snapshot to model directly.

databaseRef.once().then((DataSnapshot snapshot) {
      for(var data in snapshot.value)
        {
          var model = Model.fromJson(Map<String, dynamic>.from(data));
          logger.i(model.iponame);
        }
    });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.