Reputation: 29
I am trying to retrieve data from a document stored in Firestore. I've tried several times to find a way to fix this error but haven't been able to find something useful.
Here's my code.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class GetUserName extends StatelessWidget {
final String documentId;
GetUserName(this.documentId);
@override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('Drivers');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Object? data = snapshot.data!.data();
return Text(data!['username']);
}
return Text("loading");
},
);
}
}
The error happens when I try to retrieve 'username' from data.
Thanks for your help
Upvotes: 0
Views: 390
Reputation: 10136
The problem lies in the difference between Object
and dynamic
.
In Dart, Object?
is the "top type", which essentially means: "all values are Object?
s".
dynamic
is often used in a similar situation (when you don't know the type of the data you're working with, and don't want the type system complaining at you), but it has a very different meaning. In short, it means "turn off type checking because I know what I'm doing".
The main difference between Object?
and dynamic
comes down to their members.
Object?
has a few members defined on it:
toString()
runtimeType
hashCode
noSuchMethod(Invocation invocation)
All objects share these members, because all objects are Object?
s.
If you have a value of static type Object?
, these are the only 4 members that Dart can guarantee exist, so these are the only ones you are allowed to call (just like you'll get an error if you try to call "some string".notARealMethod()
).
dynamic
does the opposite: it assumes all possible members exist and never gives you a warning for any access, so for example:
String s = "hello";
s.notARealMethod(); // compile time error
final d = s as dynamic; // explicitly cast to dynamic
d.notARealMethod(); // now throws at runtime
So, going back to your code, you have this:
Object? data = snapshot.data!.data();
return Text(data!['username']);
data
is an Object?
, so data!
is an Object
, and as we saw before, the only 4 members on Object
are: toString()
, runtimeType
, hashCode
and noSuchMethod(Invocation invocation)
. So Dart will complain because it can't guarantee that []
exists.
To fix this:
use dynamic
instead of Object?
. It won't give you a compile error, but it will throw at runtime if the value ends up not having the []
operator (although because this is coming from firestore, this is a safe bet)
use a type check (and make use of type promotion):
Object? data = snapshot.data;
if (data is Map<String, dynamic>) {
// dart now knows that data is a Map, and now you can safely call Map methods (e.g. [])
return Text(data['username']);
}
throw "not a map";
// or
return SomeErrorWidget();
Upvotes: 4