Home > database >  How to properly retrieve data from Cloud Firestore using Flutter and Future Builder?
How to properly retrieve data from Cloud Firestore using Flutter and Future Builder?

Time:02-26

I am new to Flutter and especially Cloud Firestore. My data is stored as a Map containing another Map in my collection. To keep queries to my database as low as possible, I will store the data in a single document and want to retrieve only the lastest document. The code regarding fetching the data:

Future<Map<dynamic, dynamic>> fetchData() async {
  final _firestore = FirebaseFirestore.instance;
  late Map<dynamic, dynamic> data;

  try {
    await _firestore
        .collection('collection')
        .orderBy('timestamp', descending: true)
        .limit(1)
        .get()
        .then((value) => data = value.docs.first.data());
  } catch (e) {
    print(e);
  }
  return data;
}

Firstly, I am wondering whether there is a better way to getting the stored map from the most recent document in my collection?

Secondly,I would like to wait for this to finish and then use a FutureBuilder to show each element of my map as text, for example. At a later stage I was thinking about using a provider called in the initState that can send the data retrieved to various screens. For now, I am simply trying to display the data, but it seems like I am misunderstanding how this works:

class Home extends StatefulWidget {
  Home({Key? key}) : super(key: key);

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  late Future<Map<dynamic, dynamic>> fixtures;

  @override
  void initState() {
    fixtures = _getData();
    super.initState();
  }

  Future<Map<dynamic, dynamic>> _getData() async {
    return await fetchData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
      child: FutureBuilder(
          future: fixtures,
          builder: (BuildContext context,
              AsyncSnapshot<Map<dynamic, dynamic>> snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return Center(child: Text('loading...'));
            } else {
              if (snapshot.hasError) {
                return Center(child: Text('Error: ${snapshot.error}'));
              } else {
                return Center(child: Text('${snapshot.data!['XY']}'));
              }
            }
          }),
    ));
  }
}

It kind of works this way, but I feel like its not correct. What I don't really understand is how to properly access the data from the snapshot, how to properly store all data, and if there is a better way of doing so. The documentation always simply says snapshot.data but does not actually use the data retrieved to perform any action.

Many thanks in advance.

CodePudding user response:

It looks fine to me, if maybe a bit convoluted.

I'd probably simplify your fetchData to:

Future<Map<dynamic, dynamic>> fetchData() async {
  var value = await FirebaseFirestore.instance
        .collection('collection')
        .orderBy('timestamp', descending: true)
        .limit(1)
        .get()
  return value.docs.first.data();
}

The changes:

  • Removed the catch. Since you're not really handling the error, it's best to let it bubble up and only catch errors at the top-level, where you can then centrally log them or for example send them to Crashlytics.
  • It's often harder to read code that combines await and then. Use one of the other.
  • Related