Flutter How GridView invalid range error with Firebase - firebase

I trying to create GridView which it returns Card Widget for FutureBuilder and I have 2 fields in Firebase.
GridView's itemCount: 6
But there is 2 field in Firebase. So, it return's me something like this:
How can I solve this ?
FutureBuilder code snipped:
FutureBuilder(
future: collectionRef.get(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data != null) {
if (snapshot.data!.docs.isNotEmpty) {
return GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
physics: const BouncingScrollPhysics(),
itemCount: 6,
itemBuilder: (BuildContext context, int index) {
Map<String, dynamic> docData =
snapshot.data!.docs[index].data();
if (docData.isEmpty) {
return const Text("empty");
}
String timeSlot = docData['timeSlot'];
return Card();
},
);
} else {
return const Text("error");
}
} else {
return CircularProgressIndicator();
// const LoadingWidget1(height: 50);
}
},
),

Item depends on snapshot data length.
Replace
itemCount: 6,
with
itemCount: snapshot.data?.length,
It will provide exact data length that needs to build on GridView.
To create 6 items while handling null data
itemCount: 6,
itemBuilder: (c, index) {
if (index < snapshot.data!.length) {
//perform any index operation here
return Card(
color: Colors.grey,
);
} else {
// you cant perform because data doesnt exit on this index
/// Map<String, dynamic> docData =
// snapshot.data!.docs[index].data();
return const Card(
color: Colors.yellow,
);
}
Demo widget
body: FutureBuilder<List<int>>(
future: Future.delayed(Duration(seconds: 3))
.then((value) => List.generate(02, (i) => i)),
builder: (c, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemCount: 6,
itemBuilder: (c, index) => index < snapshot.data!.length
? Container(
color: Colors.green,
child: Text(
"${snapshot.data![index]}",
),
)
: Container(
color: Colors.red,
child: Text("Empty Box"),
),
);
}
/// handle others snapshot-state
return CircularProgressIndicator();
},
),

You have to specify itemCount to the count of fields available in Firebase.
You have 2 fields in your Firebase database and you are giving a count 6.
Replace your itemCount with this:
itemCount: snapshot.data!.docs.length,

Related

How to create streambuilder and listview with Firebase Realtime database data (Flutter chat app)

I'm building a flutter chat app for my personal learning project where the data will be retrieved from Firebase Realtime database.
I got this code from a tutorial but it is showing errors. How to solve this?
StreamBuilder(
stream: dbRef.onValue,
builder: (context, snapshot) {
if (snapshot.hasData) {
print("Error on the way");
messages.clear();
DataSnapshot dataValues = snapshot.data.snapshot; //Error: The getter snapshot is not defined for the type 'Object';
Map<dynamic, dynamic> values = dataValues.value;
values.forEach((key, values) {
messages.add(values);
});
return new ListView.builder(
shrinkWrap: true,
itemCount: messages.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Name: " + messages[index]["Text"]),
Text("Time: " + messages[index]["TextTime"]),
],
),
);
},
);
}
},
),
This solved the problem.
StreamBuilder(
stream: _dbRef.onValue,
builder: (context, snapshot) {
List<Message> messageList = [];
if (snapshot.hasData &&
snapshot.data != null &&
(snapshot.data! as DatabaseEvent).snapshot.value !=
null) {
final myMessages = Map<dynamic, dynamic>.from(
(snapshot.data! as DatabaseEvent).snapshot.value
as Map<dynamic, dynamic>); //typecasting
myMessages.forEach((key, value) {
final currentMessage = Map<String, dynamic>.from(value);
messageList.add(Message(
author: currentMessage['Author'],
authorId: currentMessage['Author_ID'],
text: currentMessage['Text'],
time: currentMessage['Time'],));
}); //created a class called message and added all messages in a List of class message
return ListView.builder(
reverse: true,
itemCount: messageList.length,
itemBuilder: (context, index) {
return ChattingDesign(
message: messageList[index],
dbpathToMsgChnl:
'TextChannels/${widget.channels['ChannelName']}/Messages',
showName: shouldShowName(
index,
messageList.length - 1,
messageList,
),
);
},
);
} else {
return Center(
child: Text(
'Say Hi...',
style: TextStyle(
color: Colors.white,
fontSize: 21,
fontWeight: FontWeight.w400),
),
);
}
},
),
According to the DataSnapshot Class Documentation there is no field called snapshot
I think there is a typo in your code.
Try this
StreamBuilder(
stream: dbRef.onValue,
builder: (context, snapshot) {
if (snapshot.hasData) {
print("Error on the way");
messages.clear();
DataSnapshot dataValues = snapshot.data! as DataSnapshot ; //here's the typo;
Map<dynamic, dynamic> values = dataValues.value;
values.forEach((key, values) {
messages.add(values);
});
return new ListView.builder(
shrinkWrap: true,
itemCount: messages.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Name: " + messages[index]["Text"]),
Text("Time: " + messages[index]["TextTime"]),
],
),
);
},
);
}
},
),

How to retrieve data from Firebase Realtime to the flutter app in a lisview

I am looking to retrieve data stored in Firebase Realtime database and display it in a new page in a lisview, how can I achieve that. So far I can retrieve and print it out in a console terminal.
My code is below:
class BarcodesResultPreviewWidget extends StatelessWidget {
FirebaseDatabase.instance.reference().child('ScannedResults');
body: Column(
children: <Widget>[
previewView,
//printing scanned results
Expanded(
child: ListView.builder(
itemBuilder: (context, position) {
return BarcodeItemWidget(preview.barcodeItems[position]);
},
itemCount: preview.barcodeItems.length,
),
),
FlatButton(
color: Colors.grey,
child: Text('Save',),
onPressed: () {
databaseRef.push().set({
'ScannedItem': preview.barcodeItems
.map((barCodeItem) => barCodeItem.toJson())
.toString(),
});
},
),
To fetch the data into a new page and build listview, try something like this:
return Scaffold(
body: FutureBuilder(
future: databaseRef.once(),
// future: FirebaseDatabase.instance
// .reference()
// .child("ScannedResults")
// .once(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return new Text('Loading....');
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
List scannedItemsValues = [];
snapshot.data.value.forEach(
(_, values) => scannedItemsValues.add(values["ScannedItem"]));
print(scannedItemsValues);
return ListView.builder(
itemCount: scannedItemsValues.length,
itemBuilder: (BuildContext context, int index) {
// build your listView here
print(scannedItemsValues[index]);
return Text(scannedItemsValues[index]);
},
);
},
),
);

How can I change a Futurebuilder into an Streambuilder?

I'm using a future builder in a method and trying to switch to a Streambuilder but struggling a it with that heres my code may be anyone can help
class _MeineFreundeState extends State<MeineFreunde> {
Icon custIcon = Icon(Icons.search);
Widget cusSearchBar = Text("Meine Freunde");
Stream myVideos;
int likes = 0;
int videos = 0;
int followers;
int following;
bool dataisthere = false;
#override
void initState() {
super.initState();
getalldata();
}
getalldata() async {
var listOfIds = [];
String myID = FirebaseAuth.instance.currentUser.uid;
var idofotheruser = await FirebaseFirestore.instance
.collection('meinprofilsettings')
.doc(myID)
.collection('following')
.get();
following = idofotheruser.docs.length;
idofotheruser.docs.forEach((element) {
listOfIds.add(element.id);
});
print(listOfIds);
myVideos = FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: 'Fp3unLwcl2SGVh4MbUPiRVAylYV2')
.snapshots();
var documents = await FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: 'Fp3unLwcl2SGVh4MbUPiRVAylYV2')
.get();
if (!mounted) return;
setState(() {
videos = documents.docs.length;
});
for (var item in documents.docs) {
likes = item.data()['likes'].length + likes;
}
var followersdocuments = await FirebaseFirestore.instance
.collection("meinprofilsettings")
.doc(myID)
.collection('followers')
.get();
var followingdocuments = await FirebaseFirestore.instance
.collection("meinprofilsettings")
.doc(myID)
.collection('following')
.get();
followers = followersdocuments.docs.length;
following = followingdocuments.docs.length;
setState(() {
dataisthere = true;
});
}
#override
Widget build(BuildContext context) {
return getBody(context);
}
Widget getBody(BuildContext context) {
return dataisthere == false
? Scaffold(body: Center(child: CircularProgressIndicator()))
: Stack(children: <Widget>[
Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
Navigator.of(context)
.pushNamed(Searchuserinmeinebeitraege.route);
},
),
],
backgroundColor: Colors.transparent,
elevation: 0.0,
),
body: RefreshIndicator(
onRefresh: _handleRefresh,
color: Colors.black,
strokeWidth: 4,
child: ListView(
children: [
Column(children: <Widget>[
SizedBox(
height: 5,
),
StreamBuilder(
stream: myVideos,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (videos > 0) {
return StaggeredGridView.countBuilder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
crossAxisCount: 3,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot video =
snapshot.data.docs[index];
return InkWell(
onTap: () {
NavigationService.instance
.navigateToRoute(MaterialPageRoute(
builder: (context) {
return VideoPage(
video.data()['videourl'],
video.data()['uid'],
video.id,
);
}));
},
child: Card(
elevation: 0.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
clipBehavior:
Clip.antiAliasWithSaveLayer,
child: Image.network(
video.data()['previewimage'],
fit: BoxFit.cover,
),
),
//imageData: searchImages[index],
),
);
},
staggeredTileBuilder: (index) =>
StaggeredTile.count(
(index % 7 == 0) ? 2 : 1,
(index % 7 == 0) ? 2 : 1),
mainAxisSpacing: 8.0,
crossAxisSpacing: 4.0,
);
} else {
return Center(
child: Padding(
padding:
const EdgeInsets.fromLTRB(0, 100, 0, 0),
child: Container(
child: Text(
"No Videos Yet",
style: TextStyle(
fontSize: 18, color: Colors.black),
),
),
),
);
}
}),
]),
],
),
),
),
]);
}
Future _handleRefresh() async {
await Future.delayed(new Duration(seconds: 2));
setState(() {
getalldata();
});
return null;
}
}
I am a beginner with flutter, I know that I just can change FuturBuilder into Streambuilder and then future to stream but what about How I'm getting the data is there any difference
I Mean something like this line
video.data()['videourl'],
Is it equal or is there any difference and also how can I change it in getalldata method. If you need more information please leave a comment.
StreamBuilder is different from FutureBuilder in many ways one main difference being
The main job of the FutureBuilder is to complete the future and return the result once the result is returned it has no way to fetch the latest snapshot from the future unless its parent rebuilds. Once the future attached returns the result the builder method gets executed to refresh the Ui.
while incase of StreamBuilder it contiuously listens to your specified collection and gets you the latest snapshot in realtime. that means any document in your firestore collection changes you get the latest updated collection and builder method rebuilds to refresh the UI.
You could use StreamBuilder to fetch data from your firestore's collection like this
String myID = FirebaseAuth.instance.currentUser.uid;
final queryVideos = await FirebaseFirestore.instance
.collection('videos')
.where('uid', arrayContains: listOfIds)
StreamBuilder<DocumentSnapshot>(
stream: queryVideos.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator()); /// show a loader
} else if (snapshot.data.docs.isEmpty) {
return const SizedBox.shrink(); // show an empty widget no docs
} else
return StaggeredGridView.countBuilder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
crossAxisCount: 3,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
/// fetch a doc by Index
final doc = snapshot.data.docs[index];
return InkWell(
onTap: () {
NavigationService.instance
.navigateToRoute(MaterialPageRoute(
builder: (context)=>VideoPage(
doc['videourl'], // this is how you access each property in a document
doc['uid'],
doc['id']
));
},
child: YourWidget());
}));
});

How can I check if a Documentsnapshot has data?

Im trying to figuring out if videos exists inside a collection or not and if they exist I wanna show them and showing them works actually but if not then I wanna print a default text like No videos yet but I dont know how I can do this . Maybe anyone can help.
Heres my code in the Inkwell I displaying all videos
FutureBuilder(
future: myVideos,
builder: (BuildContext context, snapshot) {
if (!snapshot.hasData) {
return Container();
}
return StaggeredGridView.countBuilder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
crossAxisCount: 3,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot video =
snapshot.data.docs[index];
return InkWell(
onTap: (){
Navigator.of(context).pushNamed(
ChatFirstpage.route);
},child: Card(
elevation: 0.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Image.network(
video.data()['previewimage'],
fit: BoxFit.cover,
),
),
//imageData: searchImages[index],
),
);
},
staggeredTileBuilder: (index) =>
StaggeredTile.count((index % 7 == 0) ? 2 : 1,
(index % 7 == 0) ? 2 : 1),
mainAxisSpacing: 8.0,
crossAxisSpacing: 4.0,
);
},
),
And heres my Videos collection
Future myVideos;
int likes = 0;
bool dataisthere = false;
#override
void initState() {
super.initState();
getalldata();
}
getalldata() async {
//get videos as future
myVideos = FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: widget.userid)
.get();
var documents = await FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: widget.userid)
.get();
for (var item in documents.docs) {
likes = item.data()['likes'].length + likes;
}
setState(() {
dataisthere = true;
});
}
You can do a check before navigating to the movie's page. Something like:
// ... other lines
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot video = snapshot.data.docs[index];
return InkWell(
onTap: () {
// Do your check here, if video not exist display a SnackBar
if (video.data()['video_url'] == null) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Video not exist'),
duration: Duration(seconds: 2),
backgroundColor: Colors.orange,
));
} else {
// If video exist, navigate to the page
Navigator.of(context).pushNamed(ChatFirstpage.route);
}
},
child: Card(
elevation: 0.0,
child: ClipRRect(
// ... other lines
Try this:
DocumentSnapshot video = snapshot.data.docs[index];
if (video.data()['previewimage'] == null) {return Text('no video');}

How To get all Users from FireStore Using flutter?

I want to display all the users of my firestore database as a list.
I tried this but some errors appear.
StreamBuilder(
stream:chatservice.retrieveUsers(),
builder: (ctx, usersnapshot) {
if (usersnapshot.connectionState == ConnectionState.waiting) {
return Container(
child: Center(child: CircularProgressIndicator()));
} else {
var document = usersnapshot.data;
return ListView.builder(
itemCount: document.length,
shrinkWrap: true,
padding: EdgeInsets.only(top: 16),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return ConversationList(
name: document[index]['name'],
);
},
);
}
},
)
chatservice.retrieveUsers():
Stream<QuerySnapshot> retrieveUsers() {
Stream<QuerySnapshot> queryUsers = _usercollection.snapshots();
return queryUsers;
}
i got this solution ,
this is work great ^^
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('Users').snapshots(),
builder: (ctx, AsyncSnapshot<QuerySnapshot> usersnapshot) {
if (usersnapshot.connectionState == ConnectionState.waiting) {
return Container(
child: Center(child: CircularProgressIndicator()));
} else {
return Expanded(
child: ListView.builder(
itemCount: usersnapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot document = usersnapshot.data.docs[index];
if (document.id == auth.currentUser.uid) {
return Container(height: 0);
}
return ConversationList(
name: document.data()["name"] +
" " +
document.data()["lastname"],
messageText: document.data()["lastname"],
imageUrl:
"https://cdn3.vectorstock.com/i/1000x1000/30/97/flat-business-man-user-profile-avatar-icon-vector-4333097.jpg",
isMessageRead: false,
);
},
),
);
}
},
)

Resources