I'm trying to run my flutter chat app on web but I encountered this error
════════ Exception caught by widgets library ═══════════════════════════════════
'docs'
method not found
Receiver: null
Arguments: []
The relevant error-causing widget was
StreamBuilder
Error Message Screenshot
It is working on my android emulator so maybe there's missing on my web configuration so what I've tried is adding the firebase on my index.html and following https://stackoverflow.com/a/67518124/15916393 advice but still the error occurs.
<script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-firestore.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-messaging.js"></script>
here is my code
class Messages extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = FirebaseAuth.instance.currentUser;
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('chat')
.orderBy(
'createdAt',
descending: true,
)
.snapshots(),
builder: (ctx, chatSnapshot) {
if (chatSnapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final chatDocs = chatSnapshot.data.docs ?? 'default data';
return ListView.builder(
reverse: true,
itemCount: chatDocs.length,
itemBuilder: (ctx, index) => MessageBubble(
chatDocs[index].data()['text'],
chatDocs[index].data()['username'],
chatDocs[index].data()['userImage'],
chatDocs[index].data()['userId'] == user.uid,
key: ValueKey(chatDocs[index].id),
),
);
},
);
}
}
You're ignoring the case where there is an error in loading the data
builder: (ctx, chatSnapshot) {
if (chatSnapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
// 👇 Check if there was an error
if (snapshot.hasError) {
return Text('Error loading data: ${snapshot.error!}');
}
final chatDocs = chatSnapshot.data.docs ?? 'default data';
...
I highly recommend following the patterns outline in the samples in the FlutterFire documentation like the above from reading realtime changes.
Related
I'm using Firestore and I tried to get streams by Streambuilder.
However, This error happened.
The following NoSuchMethodError was thrown building StreamBuilder<DocumentSnapshot<Object>>
(dirty, state: _StreamBuilderBaseState<DocumentSnapshot<Object>,
AsyncSnapshot<DocumentSnapshot<Object>>>#32fdb):
The method 'data' was called on null.
Receiver: null
Tried calling: data()
And this is my code.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class UserDetailPage extends StatefulWidget {
String uid;
UserDetailPage(this.uid);
#override
_UserDetailPageState createState() => _UserDetailPageState();
}
class _UserDetailPageState extends State<UserDetailPage> {
final List<String> datas = <String>['a', 'b', 'c', 'd', 'e', 'f', 'g', '1', '2','3', '4', '5', '6'];
CollectionReference userstream = FirebaseFirestore.instance.collection('users');
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('User Detail'),
),
body:_buildBody(),
);
}
_buildBody() {
return StreamBuilder(
stream: userstream.doc(widget.uid).snapshots(),
builder: (context, snapshot){
Map<String, dynamic> user_data =snapshot.data.data();
if(snapshot.hasError){
return Text('ERROR');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
return Padding(
padding: const EdgeInsets.all(20.0),
child: ListView.separated(
padding: EdgeInsets.only(left: 20, right: 20),
itemCount: 13,
separatorBuilder: (BuildContext context, int index) => const Divider(),
itemBuilder: (BuildContext context, int index){
return Center(
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(datas[index]),
Text(user_data[datas[index]] is int?user_data[datas[index]].toString():user_data[datas[index]])
],
),
),
);
}
)
);
},
);
}
}
Interesting thing is that Immediately after this error occurs, the result I wanted appears on the app.
So I thought the problem occurs in initstate() but I dont know exactly what is wrong.
By the way, this Page is called from
UserDetailPage( doc.get('uid')!=null?doc.get('uid'):'5AJUsH5LYaQcBiTtO5MA7d6OKx72');
The AsyncSnapshot wraps data that is loaded asynchronously. Calling snapshot.data without checking whether the data is available (as you do in the code below), means you are ignoring this fact and might as well not use a StreamBuilder:
stream: userstream.doc(widget.uid).snapshots(),
builder: (context, snapshot){
Map<String, dynamic> user_data =snapshot.data.data();
The proper way to deal with the stream is shown in the FlutterFire documentation on realtime listeners. The required change for you is that you only call snapshot.data after all the checks, instead of before them:
builder: (context, snapshot){
if(snapshot.hasError){
return Text('ERROR');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
Map<String, dynamic> user_data =snapshot.data.data();
I tired to get data from firestore in flutter app.
This is my code
body: StreamBuilder(
stream:
FirebaseFirestore.instance.collection('my_contact').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
return ListView.builder(
itemCount: streamSnapshot.data.docs.length,
itemBuilder: (ctx, index) => SettingRowWidget(
"Call",
vPadding: 0,
showDivider: false,
onPressed: () {
Utility.launchURL((streamSnapshot.data.docs[index]['phone']));
},
),
);
},
));
and this code getting right data but problem is i'm getting error like this.
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building StreamBuilder<QuerySnapshot>(dirty, state: _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot<QuerySnapshot>>#7adda):
The getter 'docs' was called on null.
Receiver: null
Tried calling: docs
I dont know how to solve it. Can anyone guide me to solve this?
Check if it's null while loading the data from firestore
StreamBuilder(
stream:
FirebaseFirestore.instance.collection('my_contact').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (!streamSnapshot.hasData) return Center();
if (streamSnapshot.data.docs.length!=0) {
return ListView.builder(
itemCount: streamSnapshot.data.docs.length,
itemBuilder: (ctx, index) => SettingRowWidget(
"Call",
vPadding: 0,
showDivider: false,
onPressed: () {
Utility.launchURL((streamSnapshot.data.docs[index]['phone']));
},
),
);
}else{
return Center(child:Text('No data found'));
}
},
));
I am using a Stream Provider to access Firestore data and pass it around my app. The problem I am facing starts when I first run the app. Everything starts as normal but as I navigate to the screen where I am using the Stream values in a list view, I initially get an error before the UI rebuilds and the list items appear after a split second. This is the error I get:
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building OurInboxPage(dirty, dependencies: [_InheritedProviderScope<List<InboxItem>>]):
The getter 'length' was called on null.
Receiver: null
Tried calling: length
I'm guessing this has something to do with the load time to access the values and add them to the screen? How can I load all stream values when the app starts up to avoid this?
Here is my Stream code:
Stream<List<InboxItem>> get inboxitems {
return orderCollection
.where("sendTo", isEqualTo: FirebaseAuth.instance.currentUser.email)
.snapshots()
.map(
(QuerySnapshot querySnapshot) => querySnapshot.docs
.map(
(document) => InboxItem.fromFirestore(document),
)
.toList(),
);
}
I then add this to my list of Providers:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MultiProvider(
providers: [
StreamProvider<List<InboxItem>>.value(value: OurDatabase().inboxitems),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<OurUser>(
builder: (_, user, __) {
return MaterialApp(
title: 'My App',
theme: OurTheme().buildTheme(),
home: HomepageNavigator(),
);
},
);
}
}
And finally the page I want to display the stream items:
class OurInboxPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
List<InboxItem> inboxList = Provider.of<List<InboxItem>>(context);
return Scaffold(
body: Center(
child: ListView.builder(
itemCount: inboxList.length,
itemBuilder: (context, index) {
final InboxItem document = inboxList[index];
return Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(document.event),
Icon(Icons.arrow_forward_ios)
],
),
);
},
),
),
);
}
}
Thanks
Yeah its trying to build before the data is populated, hence the null error.
Wrap your ListView.builder in a StreamBuilder and having it show a loading indicator if there's no data.
StreamBuilder<List<InboxItem>>(
stream: // your stream here
builder: (context, snapshot) {
if (snapshot.hasData) {
return // your ListView here
} else {
return CircularProgressIndicator();
}
},
);
I'm assuming your not using the latest version of provider because the latest version requires StreamProvider to set initialData.
If you really want to use StreamProvider and don't want a null value, just set its initialData property.
FROM:
StreamProvider<List<InboxItem>>.value(value: OurDatabase().inboxitems),
TO:
StreamProvider<List<InboxItem>>.value(
value: OurDatabase().inboxitems,
initialData: <InboxItem>[], // <<<<< THIS ONE
),
If you want to display some progress indicator while getter function inboxitems is executed initially. You don't need to modify the StreamProvider, and just add a null checking in your OurInboxPage widget.
class OurInboxPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final List<InboxItem>? inboxList =
Provider.of<List<InboxItem>?>(context, listen: false);
return Scaffold(
body: inboxList == null
? const CircularProgressIndicator()
: ListView.builder(
itemCount: inboxList.length,
itemBuilder: (_, __) => Container(
height: 100,
color: Colors.red,
),
),
);
}
}
There are 2 ways to solve the issue.
Use the progress bar while the data is loading.
StreamBuilder<int>(
stream: getStream(),
builder: (_, snapshot) {
if (snapshot.hasError) {
return Text('${snapshot.error}');
} else if (snapshot.hasData) {
return Text('${snapshot.data}');
}
return Center(child: CircularProgressIndicator()); // <-- Use Progress bar
},
)
Provide dummy data initially.
StreamBuilder<int>(
initialData: 0, // <-- Give dummy data
stream: getStream(),
builder: (_, snapshot) {
if (snapshot.hasError) return Text('${snapshot.error}');
return Text('${snapshot.data}');
},
)
Here, getStream() return Stream<int>.
I'm getting the error "NoSuchMethodError: The getter 'docs' was called on null. Receiver: null Tried Calling: docs" whenever I try to use .orderby("POST_ID", descending: true. If someone has an answer to why I'm getting this error and how to fix it please help!
Here is my code:
Container(
margin: EdgeInsets.only(top: 100.0),
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection("Cities")
.doc(globals.selectedCity)
.collection("Posts")
.orderBy("POST_ID", descending: true)
.where("Category", isEqualTo: globals.selectedCategory)
.snapshots(),
builder: (context, postSnapshot) {
return ListView.builder(
itemCount: postSnapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
switch (postSnapshot.data.docs[index]["Type"]) {
case "Image":
return new ImageCard(
imageLink: postSnapshot.data.docs[index]
["ImageLink"],
imageDescription: postSnapshot.data.docs[index]
["ImageDescription"],
posterName: postSnapshot.data.docs[index]
["PosterName"],
);
break;
case "Text":
return new TextCard(
postText: postSnapshot.data.docs[index]
["PostText"],
posterName: postSnapshot.data.docs[index]
["PosterName"],
);
break;
Problem is here:
(postSnapshot.data.docs[index]["Type"])
You have to ensure first that you got the data from firestore. In your case when connection state is connecting or if snapshot has error you invoke docs on null object. Consider building widget tree with checks like this:
class UserInformation extends StatelessWidget {
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return StreamBuilder<QuerySnapshot>(
stream: users.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new ListView(
children: snapshot.data.docs.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document.data()['full_name']),
subtitle: new Text(document.data()['company']),
);
}).toList(),
);
},
);
}
}
When I read a data from the Cloud Firestore.
I could get the data, but I caught the below exception.
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building StreamBuilder(dirty, state: _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot>#4f115):
The getter 'docs' was called on null.
Receiver: null
Tried calling: docs
I tried some idea on stackoverflow but I couldn't resolve.
Here is my code.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class AdminHome extends StatefulWidget {
final String email;
AdminHome({#required this.email});
#override
State<StatefulWidget> createState() {
return _AdminHomeState(this.email);
}
}
class _AdminHomeState extends State<AdminHome> {
final fireStoreInstance = FirebaseFirestore.instance;
String email;
_AdminHomeState(this.email);
#override
Widget build(BuildContext context) {
print("This email is $email");
return Scaffold(
appBar: AppBar(
title: Text("Admin Home Window"),
),
body: StreamBuilder<QuerySnapshot>(
stream: fireStoreInstance.collection(email).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
return ListView(
children: snapshot.data.docs.map((DocumentSnapshot document) {
if (snapshot.data != null && !snapshot.hasError) {
return Card(
child: ListTile(
title: Text(document.data()["gameName"]),
onTap: () {
print("tapped");
Navigator.pushNamed(context, '/adminGameDetail');
},
),
);
} else if (snapshot.data == null && !snapshot.hasError) {
return Center(child: Text('No data'));
} else {
return Center(
child: Text('Woooops'),
);
}
}).toList(),
);
},
),
);
}
}
Please help it.
snapshot.data is not yet loaded while you are trying to access it. Try this:
StreamBuilder<QuerySnapshot>(
stream: fireStoreInstance.collection(email).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if(!snapshot.hasData) return Text("Still Loading"); // return whatever widget you want to show while the data is being loaded.
if(snapshot.hasError) return Text("Error"); // Return whatever widget you want to show when an error occurs.
return ListView(
children: snapshot.data.docs.map((DocumentSnapshot document) {
if (snapshot.data != null && !snapshot.hasError) {
return Card(
child: ListTile(
title: Text(document.data()["gameName"]),
...
...
... Rest of the code