Gridview.builder with Firebase realtime database and futurebuilder - firebase

Coming from Firestore, I am a little bit struggling how to receive data from Firebase real time database. I just want a nice grid view of images which are loaded from the realtime database.
Error: flutter: The following NoSuchMethodError was thrown building:
flutter: Class 'DataSnapshot' has no instance method '[]'.
flutter: Receiver: Instance of 'DataSnapshot'
I guess it's index related. No idea how to correctly map it within a list.
import 'package:cached_network_image/cached_network_image.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
class Test extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(16.0),
child: new FutureBuilder(
future: FirebaseDatabase.instance
.reference()
.child('messages')
.child('1551276762582')
.orderByChild('messagetype')
.equalTo('1')
.once(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
if (snapshot.data != null) {
return new Column(
children: <Widget>[
new Expanded(
child: new GridView.builder(
// itemCount: item.length,
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
itemBuilder: (context, index) {
return GridTile(
child: CachedNetworkImage(
imageUrl: snapshot.data[index]['imageUrl']
.toString()));
},
),
)
],
);
} else {
return new CircularProgressIndicator();
}
} else {
return new CircularProgressIndicator();
}
}));
}
}

I could solve it with the following code. Again, I have to say that the Firebase documentation really lacks, which is quite disappointing, since Firebase is a great tool. Moreover, I do not understand, that there is no documentation on 'How to use Firebase with Flutter' (we are talking about both Google products.) Notwithstanding, here is the working code for anyone, who likes to use Streambuilder with Gridview.builder with the Realtime Database in Flutter:
StreamBuilder(
stream: FirebaseDatabase.instance
.reference()
.child('messages')
.child(groupId)
.orderByChild('messagetype')
.equalTo(1)
.onValue,
builder: (BuildContext context, AsyncSnapshot<Event> snapshot) {
if (snapshot.hasData) {
if (snapshot.data.snapshot.value != null) {
Map<dynamic, dynamic> map = snapshot.data.snapshot.value;
List<dynamic> list = map.values.toList()
..sort(
(a, b) => b['timestamp'].compareTo(a['timestamp']));
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemCount: list.length,
padding: EdgeInsets.all(2.0),
itemBuilder: (BuildContext context, int index) {
return Container(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(
imageUrl: list[index]["imageUrl"])),
);
},
child: CachedNetworkImage(
imageUrl: list[index]["imageUrl"],
fit: BoxFit.cover,
),
),
padding: EdgeInsets.all(2.0),
);
},
);
} else {
return Container(
child: Center(
child: Text(
'Es wurden noch keine Fotos im Chat gepostet.',
style: TextStyle(fontSize: 20.0, color: Colors.grey),
textAlign: TextAlign.center,
)));
}
} else {
return CircularProgressIndicator();
}
})),

Something that I do that helps me solve issues, is by explicitly turning snapshots into Maps the following way.
Map yourModel = Map.from(datasnapshot);
also many times when handling null data or so on I have to turn the asyncSnap value that comes from the future Builder into a Datasnapshot from firebase in the following way
Datasnapshot snap = asyncSnap.data;
then handle for snap being null
if(snap.value!=null){}
Hope this helps! if you need help send me a message

Related

Firebase doesn't work cause of null-safety (DART/FLUTTER)

I'm using/learning Firebase for my database works. My snapshot's coming like _jsonQuerySnapshot or _jsonDocumentSnapshot. But it had to be QuerySnapshot or DocumentSnapshot. Because of this I have to encode and decode my snapshot for use my datas.
If I'm not using encode decode json I'm getting null or object errors all the time.
Here is my class extends from state
class _MyHomePageState extends State<MyHomePage> {
final _firestore = FirebaseFirestore.instance;
#override
Widget build(BuildContext context) {
CollectionReference moviesRef=_firestore.collection('movies');
DocumentReference babaRef = _firestore.collection('movies').doc('Baba');
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: Text('FireStore Crud'),
),
body: Center(
child: Container(
child: Column(
children: [
StreamBuilder<QuerySnapshot>(
stream: moviesRef.snapshots(),
builder: (BuildContext context,AsyncSnapshot asyncSnapshot){
List<DocumentSnapshot>listOfDocumentSnapshot=asyncSnapshot.data.docs;
return Flexible(
child: ListView.builder(
itemCount: listOfDocumentSnapshot.length,
itemBuilder: (context,index){
Text('${listOfDocumentSnapshot[index].data()['name']}' ,style: TextStyle(fontSize: 24),);
},
),
);
},
),
],
),
),
),
);
}
}
and this is my error .
First of all, check your data is null or not and then use [] on it. Probably, listOfDocumentSnapshot[index].data() is null. If it is null, render another UI such as loading screen. Namely, your loading screen must be showed until reach the data.
for example:
builder: (BuildContext context,AsyncSnapshot asyncSnapshot){
List<DocumentSnapshot>? listOfDocumentSnapshot = asyncSnapshot.data.docs;
if(!listOfDocumentSnapshot.hasData || listOfDocumentSnapshot == null){
return LoadingScreen(); //etc.
}
return Flexible(
child: ListView.builder(
itemCount: listOfDocumentSnapshot.length,
itemBuilder: (context,index){
Text('${listOfDocumentSnapshot[index].data()['name']}' ,style: TextStyle(fontSize: 24),);
},
),
);
},
Futures (asynchronous programmes) need some time to get data and you have to make your UI wait until you get your data. e.g. database connections, read/write somethings to/from somewhere etc.
For more detail you can read this article.

Listview builder using with future builder with data from firestore

I am trying to use FutureBuilder to build some LisTiles with ListView.builder. The data is from Firestore.
It looks like the FutureBuilder dont access the ConnectionState.done, because I have this whole time CircularProgressIndicator() showing.
var qn;
Future<QuerySnapshot> getChargingHistory() async {
await users
.doc('$currentUser')
.collection('chargingHistory')
.get()
.then((QuerySnapshot querySnapshot) {
qn = querySnapshot;
qn.docs.forEach((doc) {
print(doc['Endzeit']);
});
});
setState(() {
});
return qn;
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Hero(
tag: 'logo',
child: Image.asset(
'assets/images/rz_bildmarke_meyer-technik_rgb.png',
height: MediaQuery.of(context).size.height * 0.05,
fit: BoxFit.cover,
),
),
actions: [],
centerTitle: true,
elevation: 4,
),
body: BackgroundContainer(
child: Column(
children: [
Expanded(
child: FutureBuilder(
future: getChargingHistory(),
builder: (context, querySnapshot) {
if (querySnapshot.connectionState ==
ConnectionState.done) {
return ListView.builder(
itemCount: qn.docs.length,
itemBuilder: (BuildContext context, index) {
return ListTile(
title: Text('${qn.docs.data['Endzeit'].toString()}'));
//Text(doc['Endzeit']);
}
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
),
First, you are mixing async/await with .then. You don't need var qn;, simply return the result of await from your getChargingHistory, like:
Future<QuerySnapshot> getChargingHistory() async {
return await users
.doc('$currentUser')
.collection('chargingHistory')
.get();
}
Second, you have to use index in itemBuilder to get the data for the current ListTile. Try:
return ListView.builder(
itemCount: querySnapshot.docs.length,
itemBuilder: (BuildContext context, index) {
return ListTile(title:
Text('${querySnapshot
.docs[index]['Endzeit'].toString()}'));
Instead of using querySnapshot.connectionState==ConnectionState.done, try using querySnapshot.hasData==true.

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]);
},
);
},
),
);

Flutter/ Firestore : Class 'QuerySnapshot' has no instance getter 'document'

I have app that I want to retreive data which are messages represented in uid document from Firestore database as explained here and these messages stored like so :
ChatRoom->chatRoomId->chat-> uid-> messages
but I receive this error :
The following NoSuchMethodError was thrown building StreamBuilder(dirty, state: _StreamBuilderBaseState<dynamic,
AsyncSnapshot>#56cb5): Class 'QuerySnapshot' has no instance
getter 'document'. Receiver: Instance of 'QuerySnapshot' Tried
calling: document
The relevant error-causing widget was: StreamBuilder
file:///Users/ahmedhussain/Downloads/khamsat/Client%20Apps/HPX-KSA/hpx_ksa/lib/Screens/messages.dart:21:12
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1 _MessagesState.chatRoomList. (package:hpxksa/Screens/messages.dart:25:38)
Here is my code:
class _MessagesState extends State<Messages> {
Stream chatRoomsStream;
Widget chatRoomList(){
return StreamBuilder(
stream: chatRoomsStream,
builder: (context, snapshot){
return snapshot.hasData ? ListView.builder(
itemCount: snapshot.data.document.length,
itemBuilder: (context, index){
return ChatRoomTile(
username: snapshot.data.documents[index].data["chatRoomId"]
.toString().replaceAll("_", "").replaceAll(Constants.myName, "replace"),
chatRoomId:snapshot.data.documents[index].data["chatRoomId"]
);
}) : Container();
}
);
}
getUserInfogetChats() {
DatabaseService().getChatRooms(Constants.myName).then((value) {
setState(() {
chatRoomsStream = value;
});
});
}
#override
void initState() {
getUserInfogetChats();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: chatRoomList(),
);
}
}
class ChatRoomTile extends StatelessWidget {
final String username;
final String chatRoomId;
ChatRoomTile({this.username, this.chatRoomId});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context)=>Conversation(chatRoomId: chatRoomId,)));
},
child: Container(
color: Colors.black26,
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
children: <Widget>[
Container(
height: 40,
width: 40,
alignment: Alignment.center,
decoration: BoxDecoration(
color: kPrimaryColor,
borderRadius: BorderRadius.circular(40),
),
child: Text("${username.substring(0,1).toUpperCase()}"),
),
SizedBox(width: 8,),
Text(username),
],
),
),
);
}
}
Here is my get function to retreive chats that contains user name:
getChatRooms(String username)async{
return await Firestore.instance.collection("ChatRoom").
where("users", arrayContains: username).
snapshots();
}
The error that you received is quite clear about what the issue is. QuerySnapshot doesn't have a document property. You likely intended to use the documents property, which is more consistent with your attempt to use a ListView.
Changing instances of snapshot.data.document to snapshot.data.documents will solve this particular issue.
return StreamBuilder(
stream: chatRoomStream,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return ChatRoomTile(
**snapshot.data.docs[index].data()['chatRoomId']**);
},
)
: Container();
},
);

Migrate Flutter app: realtime db to firestore

I am migrate flutter app from Firebase realtime database to firestore. I have trouble with update this code in chat app because firestore no have FirebaseAnimatedList.
Old code:
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(“chat“),
),
body: new Container(
child: new Column(
children: <Widget>[
new Flexible(
child: new FirebaseAnimatedList(
query: reference,
sort: (a, b) => b.key.compareTo(a.key),
padding: new EdgeInsets.all(8.0),
reverse: true,
itemBuilder: (_, DataSnapshot snapshot,
Animation<double> animation, int x) {
return new ChatMessage(
snapshot: snapshot, animation: animation);
},
),
),
New code (but give me errors):
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(“chat"),
),
body: new Container(
child: new Column(
children: <Widget>[
new Flexible(
child: new StreamBuilder<QuerySnapshot>(
stream: reference.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
return snapshot.hasData? new ListView(
physics: const AlwaysScrollableScrollPhysics(),
reverse: true,
padding: new EdgeInsets.all(8.0),
children: snapshot.data.documents.map(DocumentSnapshot snapshot) {
return new ChatMessage(
snapshot: snapshot,
animation: animation,
);
})
),
reference:
final reference = Firestore.instance.collection('messages');
Any help?
I have look up:
Firestore StreamBuilder with nested AnimatedList
How to bind a Firestore documents list to a Dropdown menu in Flutter?
How to listen for document changes in Cloud Firestore using Flutter?
Update:
Thanks everyone for response! I make some changes.
New code:
child: new StreamBuilder<QuerySnapshot>(
stream: reference.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('loading...');
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot snapshot) {
return new ChatMessage(
snapshot: snapshot,
animation: animation,
);
}).toList(),
);
}
),
),
Now only error is in animation. I have error: undefined name 'animation'
try using ListView.builder ..
new Flexible(
child: new StreamBuilder<QuerySnapshot>(
stream: reference.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
return ListView.builder(
itemCount: snapshot.data.documents.length,
reverse: false,
shrinkWrap: true,
itemBuilder: (context, index) {
return ChatMessage(
animation, snapshot.data.documents[index], index);
});
}))
Missing a bracket:
children: snapshot.data.documents.map((DocumentSnapshot snapshot) {

Resources