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
Related
I'm adding data from Firestore to a Stream from StreamBuilder, but I'm getting the following error:
Exception has occurred. StateError (Bad state: Snapshot has neither data nor error
My code.
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
AppState? estado;
static String? userID = FirebaseAuth.instance.currentUser?.uid;
static final userColeccion = FirebaseFirestore.instance.collection("users");
var groupfav = ' ';
Stream<QuerySnapshot>? taskGroup;
#override
void initState() {
super.initState();
getGroupFavData();
}
void getGroupFavData() async {
var groupFavData = await userColeccion.doc("$userID").get();
var groupfav = groupFavData.data()!['groupfav'];
taskGroup = FirebaseFirestore.instance
.collection("groups")
.doc(groupfav) // pass the obtained value
.collection("task")
.snapshots();
}
#override
Widget build(BuildContext context) {
estado = Provider.of<AppState>(context, listen: true);
return Scaffold(
appBar: AppBar(
title: const Text("Home"),
automaticallyImplyLeading: false,
),
body: StreamBuilder(
stream: taskGroup,
builder: (
BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot,
) {
if (snapshot.hasError) {
return const Text("error");
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
var data = snapshot.requireData;
return ListView.builder(
itemCount: data.size,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text("${data.docs[index]['titulo']}"),
subtitle: Text("${data.docs[index]['contenido']}"),
onTap: () {},
trailing: IconButton(
icon: const Icon(Icons.delete),
color: Colors.red[200],
onPressed: () {},
),
),
);
},
);
},
),
);
}
}
Ok, looking at your issue, I see that 1) you need to get the data of the document BEFORE you start listening on that document, which is normal, so you want to do a call first to the collection, get the document, then listen on the document's collection called task, which makes sense. Your issue is still an asynchronous issue. The app is rebuilding on a stream that still hasn't arrived; you have to fix the sequence of things.
You then need to switch things up a bit and do the following:
Option #1:
a) Use a FutureBuilder: this will allow you to make the async call to get the document name based on the user Id
b) After you get the document associated to that user, you want to listen on the stream produced by the collection called tasks in that document. There is where then you can hook up the StreamBuilder.
Option #2:
a) Keep things the way you have, but do a listen on the taskGroup snapshots; but keep rebuilding the list as the values arrive on that collection.
Those are my suggestions.
Here's some brief code on option 1:
// .. in your Scaffold's body:
Scaffold(
body: FutureBuilder( // the future builder fetches the initial data
future: userColeccion.doc("$userID").get(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
var groupfav = snapshot.data()!['groupfav'];
// then once the 'groupfav' has arrived,
// start listening on the taskGroup
taskGroup = FirebaseFirestore.instance
.collection("groups")
.doc(groupfav) // pass the obtained value
.collection("task")
.snapshots();
return StreamBuilder(
stream: taskGroup,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
// the rest of your code
});
}
return CircularProgressIndicator();
}
)
)
Option 2 would be something like:
List<Task> userTasks = [];
void getGroupFavData() async {
var groupFavData = await userColeccion.doc("$userID").get();
var groupfav = groupFavData.data()!['groupfav'];
taskGroup = FirebaseFirestore.instance
.collection("groups")
.doc(groupfav) // pass the obtained value
.collection("task")
.snapshots().listen((snapshot) {
// here populate a list of your tasks
// and trigger a widget rebuild once you've grabbed the values
// and display it as a list on the UI
setState(() {
userTasks = snapshot.docs.map((d) => Task.fromJson(d.data())).toList();
});
});
}
And in your Scaffold, you can have a ListView just rendering the items on that task list, like:
ListView.builder(
itemCount: userTasks.length,
itemBuilder: (context, index) {
// render your tasks here
})
Here's a Gist with some working code to illustrate my point. Run it on DartPad and you'll see how using a FutureBuilder wrapping a StreamBuilder will accomplish what you want.
If you run the above code on DartPad, you'll get the following output:
Hope those pointers take you somewhere.
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"],
);
} ,
);
},
),
);
}
}
Every example about StreamBuilder starts with a StatelessWidget even in flutter example but how do you cancel the subscription in a StatelessWidget widget? For example, I was going through firestore example.
class MessageList extends StatelessWidget {
MessageList({this.firestore});
final Firestore firestore;
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: firestore.collection('messages').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
final int messageCount = snapshot.data.documents.length;
return ListView.builder(
itemCount: messageCount,
itemBuilder: (_, int index) {
final DocumentSnapshot document = snapshot.data.documents[index];
return ListTile(
title: Text(document['message'] ?? '<No message retrieved>'),
subtitle: Text('Message ${index + 1} of $messageCount'),
);
},
);
},
);
}
}
Now how do I cancel listening to firestore.collection('messages').snapshots() stream?
I use realtime database in my app and this is how I do it
class MessgaeView extends StatefulWidget {
final String _chatId;
MessgaeView(this._chatId);
#override
_MessgaeViewState createState() => _MessgaeViewState();
}
class _MessgaeViewState extends State<MessgaeView> {
Stream<Event> _messageStream;
#override
void initState() {
_messageStream = _database
.reference()
.child("message/${widget._chatId}")
.limitToLast(1)
.onChildAdded;
super.initState();
}
#override
void dispose() {
_messageStream.drain();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: _messageStream,
builder: (context, snapshot) {
if (!snapshot.hasData) return CupertinoActivityIndicator();
final message =
(snapshot.data.snapshot as DataSnapshot).value['message'];
return Text(message);
});
}
}
Simply replace the previous stream instance with null.
This will require a code similar to the following somewhere:
setState(() {
_messageStream = null;
});
Doing so will stop listening to the stream. But StreamBuilder will still hold the previous value.
I hava same problem and solved by StreamSubscription
For example define StreamSubscription as global
StreamSubscription<Event> _counterSubscription;
then in your place you want to listen to data register your Subscription like this
_counterSubscription = _counterRef.onValue.listen((Event event) {
setState(() {
_counter = event.snapshot.value ?? 0;
});
});
and when you want to remove listener just make this code
if(_counterSubscription !=null)
_counterSubscription .cancel();
How would you implement a pull-down to refresh in Flutter app that gets data from a collection in Firestore preferable using a StreamBuilder or a FutureBuilder and displays it in a ListView ?
I ended up using information from here. How to refresh or reload a flutter firestore streambuilder manually?
I added the Refresh indicator and made the my stream get its data from a function see code below.
var stream;
#override
void initState() {
setState(() {
stream = mouvesStream();
});
super.initState();
}
Stream<QuerySnapshot> stream() {
return Firestore.instance.collection(_locationState).snapshots();
}
#override
Widget build(BuildContext context) {
body: StreamBuilder(
stream: stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error);
}
if (snapshot.connectionState == ConnectionState.active) {
List aList = new List();
aList.clear();
for (DocumentSnapshot _doc in snapshot.data.documents) {
Model _add = new Model.from(_doc);
aList.add(_add);
}
return TabBarView(
children: <Widget>[
RefreshIndicator(
onRefresh: _handleRefresh,
child: ListView.builder(
itemCount: aList.length,
itemBuilder: (context, index) {
return Card(aList[index]);
},
),
),
Icon(Icons.directions_transit),
],
);
} else {
return Container(
child: Center(child: 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,
...
)