The getter 'documents' was called on null - flutter

I'm using flutter and firebase to make a to-do list. It keeps showing me this error even after I checked if my snap.data == null .But I am not sure why still doesn't work.
please help. I have checked similar problems like this but still didn't solve
sorry for my English
body: StreamBuilder(
stream: Firestore.instance.collection("MyTodos").snapshots(),
builder: (context, snapshots) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshots.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot documentSnapshot =
snapshots.data.documents[index];
return Dismissible(
key: Key(index.toString()),
child: Card(
child: ListTile(
title: Text(documentSnapshot["todoTitle"]),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
todos.removeAt(index);
});
},
),
),
),
);
},
);
},
)

StreamBuilder has a default state before it gets any data at all, and you need to check for this state so you don't try to build using data that doesn't exist yet. You can do this by checking either snapshots.hasData or snapshots.data == null:
StreamBuilder(
...
builder: (context, snapshots) {
if (!snapshots.hasData) {
return CircularProgressIndicator();
}
else {
return ListView.builder(
...
);
}
},
),

Related

Data from Firestore not displaying on screen (Flutter)

I am trying to asynchronously pull data from firestore in a streambuilder (all within a list view). The problem with the approach I have now is that nothing displays on the screen. I tried setting state (after i check if the screen is mounted, but to no avail).
I know the correct data is being pulled because it prints to the screen, but for some reason, it just does not display on the screen.
here's a sample of my list view below:
Widget _showFriends() {
return Expanded(
child: StreamBuilder<List<Future<AppUser>>>(
stream: db.friendStream(),
builder: (context, snapshot) {
if(snapshot.hasData) {
print("in here");
final friends = snapshot.data!;
var friend;
String username = "";
String firstName = "";
String photoUrl = "";
return ListView.builder(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
friends[index].then((value) {
friend = value;
username = value.username;
firstName = value.firstName;
//photoUrl = value.photoUrl;
print("friend username -- " + username);
});
return GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context)
=> IndividualProfile(
sourceType: SourceType.friends,
name: friend.firstName,
userName: friend.username,
aboutMe: friend.aboutMe,
photoUrl: friend.photoUrl,
individualUid: friend.uid,
)
));
},
child: ListTile(
title: Text(username),
subtitle: Text(firstName),
/*leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(photoUrl),
),*/
),
);
},
);
}
else if(snapshot.hasError) {
print(snapshot.error.toString());
return const Center(
child: Text("An error occurred please try again later"),
);
}
else {
return const Center(
child: CircularProgressIndicator(),
);
}
}
)
);
}
Pleas let me know any suggestions. I thoroughly appreciate it!
The problem is that friends[index] is a future. You did not await it before returning GestureDetector.
return GestureDetector(...) would be called before friends[index].then callback. Variable friend would still be null when return GestureDetector(...) is called.
The correct way to wait for it would be with a FutureBuilder.
Try this:
Widget _showFriends() {
return Expanded(
child: StreamBuilder<List<Future<AppUser>>>(
stream: db.friendStream(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final friends = snapshot.data!;
return ListView.builder(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
// here I am awaiting friends[index]'s future
return FutureBuilder<AppUser>(
future: friends[index],
builder:
(BuildContext context, AsyncSnapshot<AppUser> snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text(
'An error occurred. Please try again later',
),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
final friend = snapshot.data!;
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => IndividualProfile(
sourceType: SourceType.friends,
name: friend.firstName,
userName: friend.username,
aboutMe: friend.aboutMe,
photoUrl: friend.photoUrl,
individualUid: friend.uid,
),
),
);
},
child: ListTile(
title: Text(friend.username),
subtitle: Text(friend.firstName),
// leading: CircleAvatar(
// backgroundImage:
// CachedNetworkImageProvider(friend.photoUrl),
// ),
),
);
},
);
},
);
}
if (snapshot.hasError) {
print(snapshot.error.toString());
return const Center(
child: Text('An error occurred please try again later'),
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}

Flutter Visibility widget not working third time

I have wrapped ListView.builder inside Visible widget, and the button for its visible property is in a ListTile widget with variable _currencyVisible.
The widget Visible works 2 times i.e. false/hidden(default), then changes to visible when clicked, and again hides on the second click, but it doesn't work after that. Printing on console _currencyVisible shows correct data.
Here's my code:
menuItems(BuildContext context) {
bool _currencyVisible = false;
return StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) {
return ListView(
children: [
ListTile(
title: FutureBuilder<dynamic>(
future: getUserCurrencySymbol(),
builder:(BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return Text("Currency " + snapshot.data.toString());
}),
trailing: IconButton(
icon: Icon(Icons.refresh),
onPressed: () { setState(() { _currencyVisible = !_currencyVisible; }); },
),
),
Visibility(
visible: _currencyVisible,
child: ListView.builder(
shrinkWrap: true,
itemCount:
currency.allCurrenciesList.length,
itemBuilder: (context, index) {
for (Currency c in currency.allCurrenciesList) {
currency.allCurrenciesList.removeAt(0);
return Card(
child: ListTile(
title: Text(c.country),
subtitle: Text(c.shortCurrency),
trailing: Text(c.symbol),
onTap: () {
saveUserCurrency(c.country, context);
},
),
);
}
return Text("Not Null");
},
),
),
],
);
},
);
}
You are removing all of the data from your currency list. The widget is showing correctly, but there is no data to display.
Remove this line
currency.allCurrenciesList.removeAt(0);
Don't loop through the currencies in itemBuilder. Use index instead.
Visibility(
visible: _currencyVisible,
child: ListView.builder(
shrinkWrap: true,
itemCount: currency.allCurrenciesList.length,
itemBuilder: (context, index) {
final c = currency.allCurrenciesList[index];
return Card(
child: ListTile(
title: Text(.country),
subtitle: Text(c.shortCurrency),
trailing: Text(c.symbol),
onTap: () {
saveUserCurrency(c.country, context);
},
);
}
return Text("Not Null");
),
),

Flutter Qs : The getter 'length' was called on null

So I already make Condition where if the data called is null so it will Show No Data but it still have an Error.
Here's the code
Center(
child: ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
if (data[index] == null) {
return Container(
child: Center(
child: Text(
"No News"
)
)
);
} else {
return GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) =>
DetailNewsScreen(
data: data[index],
)));
},
child: Card(
child: Row(
but it still show error
You have to check if your data variable is null, otherwise you cannot call data.length as the itemCount
You're currently checking if data[index] is not null but not data itself
You could try :
Center(
child: ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
if (data[index] == null) {
return Container(child: Center(child: Text("No News")));
} else {
// return whatever widget you want
}
}),
),
the data is null, not data[someIndex]
itemCount: data.length,
You didnt cover the case where data==null. The most straightforward way of fixing it, is to assing empty array.
if(data==null) data=[]; somewhere prior building the view.

Flutter ListView builder never completes

So im stuck on something that normally just works.
I have a firebase collection which contains a list of temmplates, i want to show these templates as a list view tile.
Stepping through the code reveals that the stream returns the items successfully , however when it reaches the itemCount property of the ListView it jumps out of the IF statement entirely and continues to display the loading indicator.
Debug console isnt showing any errors at all
StreamBuilder(
stream: firestoreDatabase.templatesStream(),
initialData: new List<AppTemplate>(),
builder: (context, snapshot) {
if (snapshot.data != null) {
List<AppTemplate> templates = snapshot.data;
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
title: Text(templates[index].title),
subtitle: Text(
templates[index].description == null ||
templates[index].description == ""
? "N/A"
: templates[index].description),
leading: Icon(Icons.collections),
trailing: GestureDetector(
onTap: () => {},
child: Icon(Icons.delete),
),
);
},
itemCount: templates.length,
);
} else if (snapshot.hasError) {
return Center(child: Text('Error'));
}
return Center(
child: CircularProgressIndicator(),
);
},
),

Flutter: When the snapshot is empty the widget disappears

I am trying to build a search list, the list is working fine but if the result is empty i need to show no data. I tried the following code but the widget holds for a second and then disappear
FutureBuilder(
future: getSearchedProducts(widget.searchString),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: Container(
child: Text('No Data Found.'),
),
);
} else {
return ListView.builder(
shrinkWrap: true,
itemCount: searchResult.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Image.network(searchResult[index].proThumbnail),
title: Text(searchResult[index].proName),
onTap: () {
print(searchResult[index].proName);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ProductPage(prodid: searchResult[index].proId);
}));
},
),
);
});
}
})
Can anyone help me with this.
Thank you in advance.
I just write the code as below and it works.
FutureBuilder(
future: getSearchedProducts(widget.searchString),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print('length of list ${searchResult.length}');
if (searchResult.length==0) {
return Center(
child: Text('No data'),
);
}
else if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
else {
return ListView.builder(
shrinkWrap: true,
itemCount: searchResult.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Image.network(searchResult[index].proThumbnail),
title: Text(searchResult[index].proName),
onTap: () {
print(searchResult[index].proName);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ProductPage(prodid: searchResult[index].proId);
}));
},
),
);
});
}
}),
In your Code in Else part before - return ListView.builder- add the following code.
...
else {
if (searchResult.length == 0)
{
return Center(child: const Text('No Date'));
}
return ListView.builder .....