I'm new to flutter and I'm trying to retrieve a list of categories from the firebase cloud firestore into my flutter application using StreamBuilder.
Im using flutter 2.8.0 and cloud_firestore: ^3.1.7
this my firestore documents: categories collection
here is my code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:harfanah/shared/loading.dart';
class categoriesMod extends StatefulWidget {
const categoriesMod({ Key? key }) : super(key: key);
#override
_categoriesModState createState() => _categoriesModState();
}
class _categoriesModState extends State<categoriesMod> {
#override
Widget build(BuildContext context) {
return Scaffold (
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('categories').snapshots(),
builder: (context, snapshot) {
if(!snapshot.hasData) {
return loading();
}
return ListView.builder(
itemCount: ,
itemBuilder: ,
);
},
),
);
}
}
I'm really confused about how can I use ItemCount and ItemBuilder. I found many many solution that say i should use something like this: itemCount: snapshot.data.documents.length but it does not work. data does not even have any attributes other than these: data attributes
You need to specify the type of the StreamBuilder then you can use the properties/methods available in the class QuerySnapshot:
class _categoriesModState extends State<categoriesMod> {
#override
Widget build(BuildContext context) {
return Scaffold (
body: StreamBuilder<QuerySnapshot<Map<String,dynamic>>>(
stream: FirebaseFirestore.instance.collection('categories').snapshots(),
builder: (context, snapshot) {
if(!snapshot.hasData) {
return loading();
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context,index){
return ListTile(
title: snapshot.data!.docs[index].data()["code"],
leading: snapshot.data!.docs[index].data()["name"],
);
} ,
);
},
),
);
}
}
Related
I would like to show all the fields of a document in firebase, but I'm having different troubles.
My intention is to show the info like a chat. I tried with a ListView.builder inside a StreamBuilder.
This is my Firestore document:
I managed to get the horizontal information using doc.data().toString but what I am trying to do is to get the whole information as a ListTile so when I add new messages they are automatically added to the ListView.builder.
This is my current code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class ChatScreen extends StatefulWidget {
const ChatScreen({Key? key}) : super(key: key);
#override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final textController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(left: 20.0),
child: StreamBuilder(
stream:
FirebaseFirestore.instance.collection("messages").snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot doc = snapshot.data!.docs[index];
return ListTile(
title: Text(doc.data().toString()),
);
},
);
}),
),
);
}
}
And this is what I get with my current code: DeviceImage
P.S. I can get the data manually if I use this but it's not what I want:
return ListTile(
title: Text(doc['sender']),
);
Try to use an Object like below.
class Message {
final String sender;
final String text;
const Message({
required this.sender,
required this.text,
});
Map<String, dynamic> toMap() {
return {
'sender': sender,
'text': text,
};
}
factory Message.fromMap(Map<String, dynamic> map) {
return Message(
sender: map['sender'] as String,
text: map['text'] as String,
);
}
}
class ChatScreen extends StatelessWidget {
const ChatScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<List<Message>>(
stream: FirebaseFirestore.instance
.collection("messages")
.snapshots()
.map((query) =>
query.docs.map((map) => Message.fromMap(map.data())).toList()),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final msgList = snapshot.data!;
return ListView.builder(
itemCount: msgList.length,
itemBuilder: (context, index) {
final message = msgList[index];
return ListTile(
title: Text(message.text),
leading: Text(message.sender),
);
},
);
});
}
}
Try with this.
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("messages")
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
return ListView.builder(
shrinkWrap: true,
primary: false,
itemCount: snapshot.hasData ? snapshot.data.docs.length : 0,
itemBuilder: (context, index) {
final data = MessageModel.fromSnapshot(snapshot.data.docs[index]);
return ListTile(
title: Text(data.text),
subtitle: Text(data.sender),
);
},
);
},
);
MessageModel
class MessageModel {
MessageModel({
this.sender,
this.text,
});
String sender;
String text;
String toRawJson() => json.encode(toJson());
factory MessageModel.fromSnapshot(DocumentSnapshot snapshot) {
final model =
MessageModel.fromJson(snapshot.data() as Map<String, dynamic>);
return model;
}
factory MessageModel.fromJson(Map<String, dynamic> json) => MessageModel(
sender: json["sender"] == null ? null : json["sender"],
text: json["text"] == null ? null : json["text"],
);
Map<String, dynamic> toJson() => {
"sender": sender == null ? null : sender,
"text": text == null ? null : text,
};
}
Hi guys im learning flutter and i have an error whenever i try to call documents in the listbuilder it gives me an error but when i tried it on a floatingactionbutton to get my texts it worked fine instead of documents i used docs it worked but for this one it doesnt work out so please help me im trying to get item counts here's my full code below
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class ChatScreen extends StatelessWidget {
const ChatScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('chats/Dx7QFvCELN2XFWumVWOY/messages')
.snapshots(),
builder: (ctx, streamSnapshot) {
return new ListView.builder(
itemCount: streamSnapshot.data.documents.length, //the error is in here
itemBuilder: (ctx, index) => Container(
padding: EdgeInsets.all(8),
child: Text("This Works"),
),
);
},
),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.add), onPressed: () {}),
);
}
}
You're probably getting a null pointer exception. Initially the builder function is called before the data is returned from Firestore. You can check for that and display another Widget while there isn't any data yet:
builder: (ctx, streamSnapshot) {
if(!streamSnapshot.hasData) return CircularProgressIndicator();
return new ListView.builder(
Also, I think you might need to use docs instead of documents. The type of streamSnapshot should be AsyncSnapshot<QuerySnapshot> and according to the documentation of QuerySnapshot, there's only a docs getter.
I'm new to Firebase and I don't know how to move forward from here now. I have an image in Firebase storage and I just want to display it in my app. In full size, so I chose Container. Now by watching a tutorial I have wrote some code but didn't get anywhere. Please can you guys help.
It maybe a silly question but you know the beginners! Thank you in advance.😅 I've updated by code with StreamBuilder.
**MAIN.DART**
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DetailScreen(),
);
}
}
**DETAILSCREEN.DART
class DetailScreen extends StatefulWidget {
#override
_DetailScreenState createState() => _DetailScreenState();
}
class _DetailScreenState extends State<DetailScreen> {
StreamSubscription<QuerySnapshot> subscription;
List<DocumentSnapshot> wallpaperList;
final CollectionReference collectionReference =
FirebaseFirestore.instance.collection("wallpaperimg");
#override
void initState() {
super.initState();
subscription = collectionReference.snapshots().listen((datasnapshot) {
setState(() {
wallpaperList = datasnapshot.docs;
});
});
}
#override
void dispose() {
subscription?.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return return StreamBuilder(
stream: FirebaseFirestore.instance
.collection("wallpaperimg")
.doc()
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("Loading");
}
var profiledetail = snapshot.data;
return Container(
child: Image(image: NetworkImage(profiledetail['imgUrl'])));
});
}
}
This is assuming that every document in firebase is storing a single imageUrl.
To display the first image in your list, you can use:
Container(
child: Image.network(wallpaperList[0].data()['imageUrl'])
)
But since you are retrieving multiple documents, not just one, you can use a ListView.builder to do that to display all the images you are getting.
ListView.builder(
itemCount: wallpaperList.length,
itemBuilder: (context, index) {
return Container(
child: Image.network(wallpaperList[index].data()['imageUrl'])
);})
Post how your data looks in firebase, to get a better answer.
-- edit 2:
Since youa re using streambuilder now, you need to pass the the document id to your query .collection('wallpaperimg').doc('theRandomNumberDocumentIdinfirebase').get()
use like this pass your stream where your images is saved.
StreamBuilder(
stream: Firestore.instance
.collection("userprofile")
.document(userid)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("Loading");
} var Profiledetail = snapshot.data;return Container(child:Image(image:NetworkImage(Profiledetail["imgurl"])));
I hope this would be Helpful
Below you can see a code where I want to display data that I have collected from a registration form in Flutter.
the registration form push the data to a collection called " user " then some other documents data are pushed as:
name - email etc...
As you can see by XXXX I want that the data I retrieve from cloud firestore be shown into the widget:
Below the code:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class WelcomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.pink,
body: MainWelcome(),
);
}
}
class MainWelcome extends StatefulWidget {
#override
_MainWelcomeState createState() => _MainWelcomeState();
}
class _MainWelcomeState extends State<MainWelcome> {
final databaseReference = Firestore.instance;
Future<QuerySnapshot> getData() async {
return await Firestore.instance.collection("user").getDocuments();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
snapshot.data.documents.forEach((element) {
Center(
child: Text(
'Benvenuta ${element.data["name"]}',
style: TextStyle(fontSize: 20, color: Colors.white),
),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return Center(child: CircularProgressIndicator());
},
);
}
}
You need to use a FutureBuilder widget to display the data in the widget tree:
Create a method that returns the data:
Future<QuerySnapshot> getData() async {
return await Firestore.instance
.collection("user")
.where("email", isEqualTo: "email_here")
.getDocuments();
}
Then inside the build() method do the following:
FutureBuilder(
future: getData(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: EdgeInsets.all(8.0),
title:
Text(snapshot.data.documents[index].data["name"]),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
),
This question already has answers here:
Flutter ListView Jumps To Top
(4 answers)
Closed 2 years ago.
Scrolling up from partway down the list makes the page jump to top. I'm using Flutter and Firestore, with a StreamBuilder to get the data.
I've tried changing scroll physics, setting placeholders, and it doesn't seem to help.
StreamBuilder<QuerySnapshot>(
// Create a stream listening to the posts collection
stream: widget.firestore
.collection('posts')
.orderBy('sequence', descending: false)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
// When we don't have data yet (!...hasData), display the text "Loading..."
if (!snapshot.hasData) return const Text('Loading...');
final int messageCount = snapshot.data.documents.length;
// When data is availible, load
return new ListView.builder(
//padding: EdgeInsets.all(3.0),
itemCount: messageCount,
itemBuilder: (_, int index) {
final DocumentSnapshot document = snapshot.data.documents[index];
if (document["type"] == "standard")
return StandardCard(widget.firestore, document.documentID);
else if (document["type"] == "text")
return TextCard(widget.firestore, document.documentID);
else if (document["type"] == "video")
return VideoCard(widget.firestore, document.documentID);
else
return Card(
// Database is incorrect
child: Center(
child: Text("[Missing sufficient information]"),
),
);
},
);
},
),
It scrolls smoothly when you scroll down, but jerks to the top on an up-scroll.
Here's a self-contained example.
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ListView Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'ListView Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
stream() async* {
yield ObjectHasFuture();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: StreamBuilder(
stream: stream(),
builder: (BuildContext context, snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
return ListView.builder(
itemBuilder: (_, int index) {
return Card(
child: snapshot.data,
);
},
);
}));
}
}
class ObjectHasFuture extends StatelessWidget {
data() async {
await Future.delayed(Duration(seconds: Random().nextInt(2)));
return Container(
height: 250,
color: Colors.green,
child: Center(
child: Text(Random().nextInt(10000).toString()),
),
);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: data(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text("Loading");
return snapshot.data;
});
}
}
Are you doing this in debug mode or release mode? Debug mode sometimes demonstrates odd artifacts that go away in the final build.
give it a scrollController:
Listview.builder(
controller: ScrollController(),
//
)
If you are dealing with static data of very low number of list items, you can speed the list up using
ListView(
addAutomaticKeepAlives: true,
...
)