Can someone please explain to me how to use itemCount from ListView.Builder which is inside of an FutureBuilder.
For now my FirebaseCloud Store only has one Document , and my app is returning an infinity list of documents,
I tried to use itemCount: snapshot.data.documents.length,
however, Get the error: Class 'DocumentSnapshot' has no instance getter 'documents'. Receiver: Instance of 'DocumentSnapshot' Tried calling: documents
EDIT it should only show this document one time and one of a kind if there is more than one document related to the UID
Here is my Code
final tabs = [
Center(
child: (Scaffold(
body: FutureBuilder(
future: FirebaseFirestore.instance
.collection('users')
.doc(uid)
.get(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.data == null)
return CircularProgressIndicator();
DocumentSnapshot manuais = snapshot.data;
if (snapshot.hasData) {
print('okkk');
print(manuais.data()['nome']);
} else {
print('nopeeeee');
}
return Container(
padding: EdgeInsets.all(16),
child: ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot manuais = snapshot.data;
return Card(
color: Colors.grey[250],
child: Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
new Image.asset(
'Images/pdflogo.png',
width: 32,
),
Center(
child: Text(
(manuais.data()['nome'].toString()),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 16),
),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: const Text(
'Compartilhar / Download'),
onPressed: () async {
var request = await HttpClient()
.getUrl(Uri.parse(manuais
.data()['documento']));
var response =
await request.close();
Uint8List bytes =
await consolidateHttpClientResponseBytes(
response);
await Share.file(
'ESYS AMLOG',
'Manual.pdf',
bytes,
'image/jpg');
}),
],
),
],
),
),
);
}),
);
})))),
````
You have to do:
itemCount : 1
Since you are only retrieving one document, when you do:
FirebaseFirestore.instance
.collection('users')
.doc(uid)
.get()
The above will always retrieve one document since the document id will always be unique.
You need to get length like this.
itemCount: snapshot.data.data().length,
if (snapshot.hasData) {
print('okkk');
print(manuais.data()['nome']);
return Container(
padding: EdgeInsets.all(16),
child: ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot manuais = snapshot.data;
return Card(
color: Colors.grey[250],
child: Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
new Image.asset(
'Images/pdflogo.png',
width: 32,
),
Center(
child: Text(
(manuais.data()['nome'].toString()),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 16),
),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: const Text(
'Compartilhar / Download'),
onPressed: () async {
var request = await HttpClient()
.getUrl(Uri.parse(manuais
.data()['documento']));
var response =
await request.close();
Uint8List bytes =
await consolidateHttpClientResponseBytes(
response);
await Share.file(
'ESYS AMLOG',
'Manual.pdf',
bytes,
'image/jpg') ...(all your brackets go here)
}
Related
I don't understand how to change dynamically widget color constantly based on the real-time data from the socket.io. I want to change the widget color when data will come from the database.
Widget build(BuildContext context) {
return FutureBuilder<Room>(
future: roomService.getRoom(),
builder: (context, snapshot){
if(snapshot.hasData){
return GridView.builder(
itemCount: snapshot.data!.data.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5),
itemBuilder: (context, index){
var values = snapshot.data!.data[index];
return GestureDetector(
onTap: (){
Navigator.push(context, CupertinoModalPopupRoute(builder: (context)=>RoomDetailScreen(20, 30, 20+30)));
},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.r),
),
color: Colors.green, //have to change here
elevation: 6,
child: Container(
alignment: Alignment.center,
child:Text(
"${values['RType']}",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.sp,
color: AppColors.whiteSec),
),
),
),
);
},
);
}else if(snapshot.hasError){
return Center(
child: Column(
children: const [
Center(child: Icon(Icons.error,color: AppColors.yellowPri,)),
],
),
);
}
return const Center(child: CircularProgressIndicator());
}
);
}
If you just have to switch between 2 colours depending on the data.
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.r),
),
color: snapshot.data == 'Room-1' ? Colors.green : Colors.blue,
elevation: 6,
assuming that one of your data values is 'Room-1'.
If you'll be switching among multiple colours...
builder: (context, snapshot){
Map<String, Colour> colours = {
'Room-1': Colours.green,
'Room-2': Colours.blue,
'Room-3': Colours.red,
builder: (context, snapshot){
if(snapshot.hasData){
return GridView.builder(
itemCount: snapshot.data!.data.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5),
itemBuilder: (context, index){
var values = snapshot.data!.data[index];
return GestureDetector(
onTap: (){
Navigator.push(context, CupertinoModalPopupRoute(builder: (context)=>RoomDetailScreen(20, 30, 20+30)));
},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.r),
),
color: Colors.green, //have to change here
elevation: 6,
child: Container(
};
if(snapshot.hasData){
return GridView.builder(
itemCount: snapshot.data!.data.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5),
itemBuilder: (context, index){
var values = snapshot.data!.data[index];
return GestureDetector(
onTap: (){
Navigator.push(context, CupertinoModalPopupRoute(builder: (context)=>RoomDetailScreen(20, 30, 20+30)));
},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.r),
),
color: colours[snapshot.data],
elevation: 6,
child: Container(
Note: Your question says that you want to change the colour 'when data will come from the database'. But since you are already using snapshot.hasData, the GridView itself won't be rendered without any data from the DB.
I got a collection of questions in Firebase. The Categories are stored in an array. Now I want to to hide specific questions, if the user chose to hide it (User ID is stored in another array called hidden). This is what I tried:
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("question")
.where('category', arrayContainsAny: [widget.cid])
.where('hidden', '!=', '${user.uid}')
// also tried where('hidden', isNotEqualTo: [user.uid])
.snapshots(),
builder: (context, snapshot) {
return !snapshot.hasData
? Center(child: CircularProgressIndicator())
: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot data = snapshot.data!.docs[index];
return QuestionList(
de_du: data['de_du'],
de_sie: data['de_sie'],
de_ich: data['de_ich'],
qid: data['id'],
en: data['en'],
id: data.id,
documentSnapshot: data,
);
},
);
},
),
Any suggestions on how to get there?
Updated:
I tried to implement the transformation. I still get errors and do not get it to work. The errors I get: the first snapshot in transform is undefined. Expects an ) right after the first snapshot in transform. Also I get the info, that the user (provider of userid) is not used.
class _CategoryDetailState extends State<CategoryDetail> {
#override
Widget build(BuildContext context) {
var user = Provider.of<AuthProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text(
this.widget.titel,
style: TextStyle(fontFamily: 'Gruppo'),
),
),
body: ListView(
children: [
Card(
margin: EdgeInsets.all(10),
child: Column(
children: [
Image.network(this.widget.image),
ListTile(
title: Text(this.widget.titel),
subtitle: Text(this.widget.description),
),
],
),
),
Center(
child: Text(
'Fragenübersicht:',
style: TextStyle(fontFamily: 'Caveat', fontSize: 25),
),
),
StreamBuilder<List<DocumentSnapshot>>(
stream: FirebaseFirestore.instance
.collection("question")
.where('category', arrayContainsAny: [widget.cid])
.snapshots()
.transform(snapshot => snapshot.docs.where(d => doc.data().hidden.contains(user.userid).toList())),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return ListView.builder(
scrollDirection: Axis.vertical,
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
DocumentSnapshot data = snapshot.data![index];
return QuestionList(
de_du: data['de_du'],
de_sie: data['de_sie'],
de_ich: data['de_ich'],
qid: data['id'],
en: data['en'],
id: data.id,
documentSnapshot: data,
);
},
);
}
},
),
],
),
);
}
}
There is no way to exclude a single item from a Firestore query with a condition. What you'd need is an array-does-not-contain condition, which doesn't exist.
So that means that you'll have to read the additional items from the database, and then exclude them from the stream with something like:
StreamBuilder<List<DocumentSnapshot>>(
stream: FirebaseFirestore.instance
.collection("question")
.where('category', arrayContainsAny: [widget.cid])
.where('hidden', '!=', '${user.uid}')
.snapshots()
.transform(snapshot => snapshot.docs.where(d => doc.data().hidden.contains(user.uid)).toList(),
You'll need to update the builder too to reflect that it now gets List<DocumentSnapshot> instead of QuerySnapshot.
I am building a flutter app that fetches documents through firebase cloud firestore but i want it to show only the documents written that day. Like if i add to the collection, it will return only the documents for today. Please help
Container(
height: MediaQuery.of(context).size.height,
child: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('all').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('.....Loading');
}
return ListView.builder(
scrollDirection: Axis.vertical,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot all = snapshot.data.documents[index];
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
children: [
Text('10:00', style: TextStyle(fontSize: 18),),
SizedBox(height: 3,),
Text('05 Sep', style: TextStyle(fontSize: 13),),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Europa league', style: TextStyle(fontSize: 15),),
SizedBox(height: 2,),
Text( '${all['gg']['home']} vs ${all['gg']['away']}', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),),
you just need to put a where clause on the query like so (this gives anything made in last 24 hours):
Firestore.instance
.collection("all")
.where("created_date", isGreaterThan: DateTime.now().subtract(Duration(days: 1)) )
.snapshots();
Make sure that your data has dates, when inserting you can do it like so:
Firestore.instance
.collection("all")
.add({
'data': "data",
'created_date' : FieldValue.serverTimestamp(),
});
I have saved my phone's call list to a database and now I want to create a ListView from this database. I have already written this code. But it is not working. what is wrong with my code?
database_helper.dart
...
Future<List<Map<String, dynamic>>> queryAllRows() async {
Database db = await instance.database;
return await db.query(table);
}
...
my_home.dart
...
FutureBuilder(
future: db.queryAllRows(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
List entries = snapshot.data.toList();
return Scrollbar(
child: ListView.builder(
itemBuilder: (context, index) {
var entry = entries[index];
var mono = TextStyle(fontFamily: 'monospace');
return Column(
children: <Widget>[
Divider(),
Text('NUMBER : ${entry.number}', style: mono),
Text('NAME : ${entry.name}', style: mono),
Text('TYPE : ${entry.callType}', style: mono),
Text(
'DATE : ${DateTime.fromMillisecondsSinceEpoch(entry.timestamp)}',
style: mono),
Text('DURATION : ${entry.duration}',
style: mono),
],
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
);
},
itemCount: entries.length,
),
);
})
...
You need to use ConnectionState inside your builder. Look at this code template: (Currently the builder return your widget without waiting for the future to complete)
return FutureBuilder<List<Map<String, dynamic>>>(
future: db.queryAllRows(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// future complete
if (snapshot.hasError || !snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
// future complete with no error and has data
List entries = snapshot.data.toList();
return Scrollbar(
child: ListView.builder(
itemBuilder: (context, index) {
var entry = entries[index];
var mono = TextStyle(fontFamily: 'monospace');
return Column(
children: <Widget>[
Divider(),
Text('NUMBER : ${entry.number}', style: mono),
Text('NAME : ${entry.name}', style: mono),
Text('TYPE : ${entry.callType}', style: mono),
Text(
'DATE : ${DateTime.fromMillisecondsSinceEpoch(entry.timestamp)}',
style: mono),
Text('DURATION : ${entry.duration}', style: mono),
],
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
);
},
itemCount: entries.length,
),
);
}
// return loading widget while connection state is active
else {
return Center(child: CircularProgressIndicator());
}
});
I want to be able to create a list from an array inside a document in firestore database.
My database is like so:
I want to be able to create a streamBuilder grid from the urls inside the videosUrl array.
I tried a lot of things, but I guess that the best way is something like this one:
StreamBuilder <List<DocumentSnapshot>>(
stream: Firestore.instance
.collection('events')
.document('-LeH4rspnPTpeTLdt8hs')
.collection('participants')
.document('-LeL_TSfFDfqKgm-Io9T')
.snapshots().asyncMap((snap) async {
List<String> videosUrlArray = snap.data['videosUrl'];
var videoUrlList = <DocumentSnapshot>[];
for (var videoUrlPath in videosUrlArray) {
videoUrlList.add(await Firestore.instance.document(videoUrlPath).get());
}
print(videoUrlList);
return videoUrlList;
}),
builder: (BuildContext context, AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting: return _showLoading();
default:
return new GridView(
reverse: false,
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
children: snapshot.data.map((DocumentSnapshot document) {
return Text('${snapshot.data}');
}).toList(),
);
}
},
)
but still I can't access the data!
I resolve this issue with the code below:
Widget buildGridFilesToExport(){
return new StreamBuilder(
stream: Firestore.instance
.collection('users')
.document(dataUserGlobal.userAdminId)
.collection('events')
.document(dataUserGlobal.eventId)
.snapshots(),
builder: (context, snapshot) {
print(snapshot);
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting: return new Text('Loading...');
default:
List videosList = snapshot.data['thumbnailsUrl'];
return
videosList != null ?
new GridView.count(
crossAxisCount: 2,
childAspectRatio: 1,
children: List.generate(snapshot.data['thumbnailsUrl'].length, (index) {
return Container(
padding: EdgeInsets.all(5.0),
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 2.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
image: DecorationImage(
image: NetworkImage(snapshot.data['thumbnailsUrl'][index]),
fit: BoxFit.cover,
),
),
),
],
)
);
}),
)
:
Center(
child: Container(
width: 300,
child: Text(
'Ancora nessun video!\nVai nella cartella amici e accetta i loro video!',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'acumin-pro',
fontSize: 22,
),
),
)
);
}
},
);
}