i want to make a grid of image by 3 like instagram based on user upload. the streambuilder is not displaying any data from firestore.
i already separate the stream so its not inside the streambuilder.
this is the code
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
File? imageFile;
String? imageUrl;
final FirebaseAuth _auth = FirebaseAuth.instance;
String? myImage;
String? myName;
String buttonName = 'click';
int currentIndex = 0;
final icon = CupertinoIcons.chat_bubble_2;
final _constructed = FirebaseFirestore.instance
.collection('fotoupload')
.orderBy("createAt", descending: true)
.snapshots(); //construct stream first
//final Stream<QuerySnapshot> _constructed = FirebaseFirestore.instance
// .collection('fotoupload')
// .orderBy("createAt", descending: true)
// .snapshots();
void read_userInfo() async {
FirebaseAuth.instance.currentUser!;
FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.get()
.then<dynamic>((DocumentSnapshot snapshot) async {
myImage = snapshot.get('userImage');
myName = snapshot.get('name');
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
read_userInfo(); // refresh and read the user info both myName and myImage
}
Widget gridViewWidget(String docId, String img, String userImg, String name,
DateTime date, String userId, int downloads) {
return GridView.count(
primary: false,
padding: EdgeInsets.all(5),
crossAxisSpacing: 1,
crossAxisCount: 1,
children: [
GestureDetector(
onTap: () {
//createOwnerDetails
},
child: Center(
child: Text(date.toString()),
),
),
GestureDetector(
onTap: () {
//createOwnerDetails
},
child: Image.network(
img,
fit: BoxFit.cover,
),
),
Center(child: Text(userId)),
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: _constructed,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.data!.docs.isNotEmpty) {
return GridView.builder(
itemCount: snapshot.data!.docs.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return gridViewWidget(
snapshot.data!.docs[index].id,
snapshot.data!.docs[index]['Image'],
snapshot.data!.docs[index]['userImage'],
snapshot.data!.docs[index]['name'],
snapshot.data!.docs[index]['createAt '].toDate(),
snapshot.data!.docs[index]['uid'],
snapshot.data!.docs[index]['downloads'],
);
},
);
} else {
return Center(
child: Text(
'There is no tasks',
style: TextStyle(fontSize: 20),
),
);
}
}
return Center(
child: Text(
'Something went wrong',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
);
},
),
);
}
}
does anyone have suggestion? the streambuilder is not displaying the data. and its shows "There is no task".
You are reconstructing the stream again after every build, I recommend you to construct the stream in a private variable then use it in the stream in place of
FirebaseFirestore.instance.collection('fotoupload').orderBy("createAt", descending: true).snapshots()
as documented here It is implemented like this
class _HomePageState extends State<HomePage> {
final _constrcted = FirebaseFirestore.instance
.collection('fotoupload')
.orderBy("createAt", descending: true)
.snapshots();
//....
#override
Widget build(BuildContext context) {
return Scaffold(body: StreamBuilder<QuerySnapshot>(
stream: _constrcted,
builder: (context, snapshot) {
//...
}
stream: FirebaseFirestore.instance
.collection('fotoupload')
.orderBy("createAt", descending: true)
.snapshots(),
There's your prooblem. Do not create the stream in the stream: parameter of a StreamBuilder. It will get re-created on each call to build(), up to 60 times per second if there's an animation on the page. This doesn't give time for any data to appear... in fact it throws it away on each rebuild.
Follow the advice from the FutureBuilder and StreamBuilder documentation: create and initialize the stream in the state and initState of your widget. The value must remain constant for StreamBuilder to do its job.
I have more details and a demonstration at https://youtu.be/sqE-J8YJnpg.
Related
The code below is what I am trying now. The page works does everything I need but now I need this database reference to use the loanuid, clientuid, and companyName to get to the right directory.
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('prosperitybank')
.doc('OHViYK8Zz6XfKGJsSXRL')
.collection('Project Information')
.snapshots()```
I need it from my collection.(userCreationRef).doc(loggedinuid) as shown in the picture. I can not figure out how to do this without the stream builders interfering any help would be greatly appreciated. I have tried to using this to help but it did not How can you nest StreamBuilders in Flutter?. I also tried looking at the documentation here https://firebase.flutter.dev/docs/firestore/usage/.
Picture of Document I need data fields from
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:photoloanupdated/screens/mains/viewproperties.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
final FirebaseAuth auth = FirebaseAuth.instance;
final user = auth.currentUser;
final uid = user?.uid;
var users = FirebaseFirestore.instance.collection('userCreationRequests');
var companyname = "";
return Scaffold(
appBar: AppBar(
title: Text(companyname),
),
body:
FutureBuilder<DocumentSnapshot>(
future: users.doc(uid).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Text("Full Name: ${data['companyName']} ${data['last_name']}");
}
return Text("loading");
},
);
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('prosperitybank')
.doc('OHViYK8Zz6XfKGJsSXRL')
.collection('Project Information')
.snapshots(), //key spot fV or email fix
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data?.docs.length,
itemBuilder: (BuildContext context, int index) {
QueryDocumentSnapshot<Object?>? documentSnapshot =
snapshot.data?.docs[index];
//for date/time DateTime mydateTime = documentSnapshot['created'].toDate();
return InkWell(
onTap: () {
Navigator.of(context)
.push(
MaterialPageRoute(
builder: (context) => ViewProperties(documentSnapshot,
snapshot.data?.docs[index].reference)),
)
.then((value) {
setState(() {});
});
},
child: Card(
child: Container(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${documentSnapshot!['address']}",
style: TextStyle(
fontSize: 24.0,
fontFamily: "lato",
fontWeight: FontWeight.bold,
color: Colors.black),
),
Container(
alignment: Alignment.centerRight,
child: Text(
"${documentSnapshot!['projectcomplete'].toString() + "% Complete"}",
// for mydateTime.toString(),
style: TextStyle(
fontSize: 17.0,
fontFamily: "lato",
color: Colors.black87),
),
)
],
),
),
),
),
);
},
);
} else {
return Center(
child: Text("Loading..."),
);
}
},
),
);
}
}
String uuid;
Future<List<Map<String, dynamic>>> _onQuery() {
Future<List<Map<String, dynamic>>> res;
if (uuid != null) {
res = future.get().then((v) => v.docs
.map((e) => e.data())
.where((e) =>
e['uuid'].toLowerCase().contains(uuid))
.toList());
} else {
res = future.get().then((v) => v.docs.map((e) => e.data()).toList());
}
setState(() {});
return res;
}
now you can use _onQuery as stream.
I have implemented in my flutter application Authentication via Firebase by login and password.
I also connected to a Firestore table with user data.
I only need to show the authenticated user's data by their ID, which are assigned to the other data tables.
How to link e.g. Users id: 1 with Tasks User id:1 ?
home_page.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:loginui/read_data/get_user_name.dart';
class MyTaskPage extends StatefulWidget {
const MyTaskPage({Key? key}) : super(key: key);
#override
State<MyTaskPage> createState() => _MyTaskPageState();
}
class _MyTaskPageState extends State<MyTaskPage> {
final user = FirebaseAuth.instance.currentUser!;
// document IDs
List<String> docIDs = [];
// get docIDs
Future getDocId() async {
await FirebaseFirestore.instance
.collection('tasks')
.orderBy('name', descending: true)
// .where('age', isGreaterThan: 44)
.get()
.then((snapshot) => snapshot.docs.forEach((document) {
print(document.reference);
docIDs.add(document.id);
}));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple[200],
centerTitle: true,
actions: [
GestureDetector(
onTap: () {
setState(() {
FirebaseAuth.instance.signOut();
});
},
child: Icon(Icons.logout),
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: FutureBuilder(
future: getDocId(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: docIDs.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
title: GetUserName(documentId: docIDs[index]),
tileColor: Colors.grey[200],
),
);
},
);
},
),
),
],
),
),
);
}
}
get_user_name.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class GetUserName extends StatelessWidget {
final String documentId;
GetUserName({required this.documentId});
#override
Widget build(BuildContext context) {
// get the collection
CollectionReference users = FirebaseFirestore.instance.collection('tasks');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder: ((context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Text(
'${data['name']}',
overflow: TextOverflow.ellipsis,
softWrap: true,
);
}
return Text('Loading...');
}),
);
}
}
final _auth = FirebaseAuth.instance;
final _firestore = FirebaseFirestore.instance;
if (_auth.currentUser != null) {
// user is signed in
final uid = _auth.currentUser!.uid;
final userData = await _firestore.collection('users').doc(uid).get();
final taskData = await _firestore.collection('tasks').doc(uid).get();
// handle the data
}
As the following animation displays, when I tap one of the list items that StreamBuilder() is querying, it shows the items data on the right darker container (it's always Instance of '_JsonQueryDocumentSnapshot'). But at the same time in each tap, the whole list is refreshing itself, which is not very cost-effective I believe.
How can I avoid this unwanted refresh?
Answers with GetX state management dependency are also welcome.
class Schedule extends StatefulWidget {
#override
_ScheduleState createState() => _ScheduleState();
}
class _ScheduleState extends State<Schedule> {
final FirebaseFirestore _db = FirebaseFirestore.instance;
final DateTime _yesterday = DateTime.now().subtract(Duration(days: 1));
var _chosenData;
#override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: _db.collection('Schedule').where('date', isGreaterThan: _yesterday).limit(10).orderBy('date').snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
var data = snapshot.data!.docs[index];
return ListTile(
leading: Icon(Icons.person),
title: Text(data['project'], style: TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(data['parkour']),
onTap: () {
setState(() {_chosenData = data;});
},
);
},
);
} else {
return Center(child: CupertinoActivityIndicator());
}
},
),
),
VerticalDivider(),
Expanded(
child: Container(
alignment: Alignment.center,
color: Colors.black26,
child: Text('$_chosenData'),
),
),
],
);
}
}
To me the easiest solution would be just make it stateless and use a Getx class.
class ScheduleController extends GetxController {
var chosenData;
void updateChosenData(var data) {
chosenData = data;
update();
}
}
And your Schedule.dart would look like this:
class Schedule extends StatelessWidget {
final FirebaseFirestore _db = FirebaseFirestore.instance;
final DateTime _yesterday = DateTime.now().subtract(Duration(days: 1));
#override
Widget build(BuildContext context) {
final controller = Get.put(ScheduleController());
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: _db
.collection('Schedule')
.where('date', isGreaterThan: _yesterday)
.limit(10)
.orderBy('date')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
var data = snapshot.data!.docs[index];
return ListTile(
leading: Icon(Icons.person),
title: Text(data['project'],
style: TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(data['parkour']),
onTap: () => controller.updateChosenData(data), // calls method from GetX class
);
},
);
} else {
return Center(child: CupertinoActivityIndicator());
}
},
),
),
VerticalDivider(),
Expanded(
child: Container(
alignment: Alignment.center,
color: Colors.black26,
child: GetBuilder<ScheduleController>(
builder: (controller) => Text('${controller.chosenData}'), // only this rebuilds
),
),
),
],
);
}
}
This way the listview.builder never rebuilds, only the Text widget directly inside the GetBuilder gets rebuilt when you selected a different ListTile.
Calling setState() notifies the framework that the state of Schedule has changed, which causes a rebuild of the widget and so your StreamBuilder.
You could move your stream logic to an upper level of the widget tree. So, setState() will not trigger a rebuild of StreamBuilder.
class ParentWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('Schedule')
.where(
'date',
isGreaterThan: DateTime.now().subtract(Duration(days: 1)),
)
.limit(10)
.orderBy('date')
.snapshots(),
builder: (context, snapshot) {
return Schedule(snapshot: snapshot); // Pass snapshot to Schedule
},
);
}
}
Another approach would be using Stream.listen in initState() which is called once. This way your stream won't be subscribed for each time setState() is called.
...
late StreamSubscription<QuerySnapshot> _subscription;
#override
void initState() {
_subscription = _db
.collection('Schedule')
.where('date', isGreaterThan: _yesterday)
.limit(10)
.orderBy('date')
.snapshots()
.listen((QuerySnapshot querySnapshot) {
setState(() {
_querySnapshot = querySnapshot;
});
});
super.didChangeDependencies();
}
#override
void dispose() {
_subscription.cancel(); // Cancel the subscription
super.dispose();
}
...
I have collection where i want to get fields. I have followed instructions on flutter fire and it says to use get() to retrieve documentsnapshot. I am creating a documentsnapshot object and trying to get all for a given id. Now i am trying to pass this stream into streambuilder and display specific fields. But i am getting error;
type '_BroadcastStream<DocumentSnapshot<Map<String, dynamic>>>' is not a subtype of
type
'Stream<QuerySnapshot<Object?>>?'
My code is;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:quicktodo/model/authservice.dart';
class events extends StatefulWidget {
analytics({Key? key}) : super(key: key);
String uida = FirebaseAuth.instance.currentUser!.uid;
#override
_analyticsState createState() => _analyticsState();
}
class _ eventsState extends State<analytics> {
late final _stream;
String uid = FirebaseAuth.instance.currentUser!.uid;
void initState() {
super.initState();
setState(() {
_stream = FirebaseFirestore.instance.collection('events').doc(uid).get();
});
}
#override
Widget build(BuildContext context) {
final authService = Provider.of<AuthClass>(context);
return Scaffold(
appBar:AppBar(
leading:
IconButton(
icon: Icon(Icons.logout),
tooltip: 'List of your activities',
onPressed: () {
authService.signout(
);
},
),
title: const Text('Activity list'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.add),
tooltip: 'List of your activities',
onPressed: () {
},
),
],
),
body: StreamBuilder(
stream: _stream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
// final trip = DataModel.fromSnapshot(document);
Map a = document.data() as Map<String, dynamic>;
a['id'] = document.id;
return Container(
child: Text(a['eventone']),
);
}).toList(),
);
},
),
);
}
}
Replace your stream builder with this:
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('events')
.doc(uid)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) return Text('Something went wrong');
if (snapshot.connectionState == ConnectionState.waiting)
return Text("Loading");
Map data = snapshot.data.data();
print(data['eventide']); // should print 4
print(data['eventtwo']); // should print 5
return SizedBox();
},
);
I changed your initial stream builder because it will only work for collection snapshot, not document snapshot.
At first, when i started writing my calls to get data from firestore, it worked. But when i tried writing more docs to my collection, it failed to bring data for the docs i recently added. Then, when i deleted the first one i added, i stopped receiveing data from firestore all together. I have tried several methods, but have all ended in failure.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class collect extends StatefulWidget {
#override
_collectState createState() => _collectState();
}
class _collectState extends State<collect>
{
Future _data;
void initState()
{
super.initState();
_data = getStuff();
}
Future getStuff()
async {
var firestore = FirebaseFirestore.instance;
QuerySnapshot qn = await firestore.collection("buses").get();
return qn.docs;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _data,
builder: (_, snapshot)
{
if(snapshot.connectionState == ConnectionState.waiting)
{
return Center(
child:Text("Loading")
);
}
else if(snapshot.connectionState == ConnectionState.done)
{
return ListView.builder(itemCount: snapshot.data.length,itemBuilder:(_, index)
{
return Container(
child: ListTile(
title: Text(snapshot.data[index].data()["name"].toString()),
subtitle: Text(snapshot.data[index].data()["price"].toString()),
),
);
});
}
},
),
);
}
}
```![enter image description here](https://i.stack.imgur.com/L7FqF.jpg)
Define your database call as,
Future getStuff() async {
var docs;
await FirebaseFirestore.instance
.collection("buses")
.get()
.then((querySnapshot) {
docs = querySnapshot.docs;
});
return docs;
}
Then use the FutureBuilder in the build() function as,
return Scaffold(
body: Center(
child: FutureBuilder<dynamic>(
future: getStuff(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
return Container(
child: ListTile(
title: Text(
snapshot.data[index].data()["name"].toString()),
subtitle: Text(
snapshot.data[index].data()["price"].toString()),
),
);
});
} else {
return CircularProgressIndicator();
}
},
),
),
);
I wrapped the FutureBuilder inside a Center just for clarity, you may remove that Center widget.