FutureBuilder get data only one time - flutter

I have to get a List from Realtime Database (Firebase). I have no problem to get first time my list but if I go back and return on the future builder activity I get no data.
This is my code:
import 'package:cleverpot/Utily/Constants.dart';
import 'package:cleverpot/Utily/Function.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
class AxisChart extends StatelessWidget {
AxisChart(this.title);
final String title;
final FirebaseDatabase db =
FirebaseDatabase(databaseURL: Constans.url_database);
final FirebaseAuth _auth = FirebaseAuth.instance;
List records = [];
Future getData() async {
Map data = {};
db
.reference()
.child(_auth.currentUser.uid)
.child("records")
.once()
.then((value) {
data = Map.of(value.value);
records = data[FunctionHelper.selectFolder(title)];
});
}
#override
Widget build(BuildContext context) {
return FutureBuilder<dynamic>(
future: getData(),
builder: (context, snapshot) {
return Center(
child: SfCartesianChart(
title: ChartTitle(text: "Ultima settimana"),
primaryXAxis: CategoryAxis(),
series: <LineSeries<Records, int>>[
LineSeries(
dataSource: <Records>[
Records(1, records[0]),
Records(2, records[1]),
Records(3, records[2]),
Records(4, records[3]),
Records(5, records[4]),
Records(6, records[5]),
Records(7, records[6]),
],
xValueMapper: (Records records, _) => records.day,
yValueMapper: (Records records, _) => records.records)
],
),
);
});
}
}
class Records {
Records(this.day, this.records);
final int day;
final int records;
}
If I use listen method in getData() function instead of once I resolve the problem but i don't want to stay on listen for the data. Anyone can help me? Thanks a lot!

For this example, I will use firebase firestore but I think the procedure is similar
1. Creating a stream
stream = FirebaseFirestore.instance.collection('users').snapshots();
2. Real-time read using StreamBuilder
return StreamBuilder<QuerySnapshot>(
stream: stream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading"); // this will prevent from null error
}
//when succeedeed to get data, you can make some list to show them
//
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['full_name']),
subtitle: Text(data['company']),
);
}).toList(),
);
source code from here

Related

rebuilding listview.builder every time I scroll , and 'Stream has already been listened to' error

I am using a stream builder which has another stream builder inside it. Every time I get data from the first stream I use some of this data in the other stream to finally build a list view (POSTS), but I have a problem every time I scroll down I have this error:
if (!_isInitialState) {
throw StateError("Stream has already been listened to.");
}
I tried to listen to the second stream asBroadcastStream(), and I added the case that there is no data and every time I scroll I get the notification I made that there is no data any ideas?
This is my code:
StreamBuilder<QuerySnapshot>(
stream: posts.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
return SizedBox(
height: MediaQuery.of(context).size.height * 0.69,
child: ListView(
scrollDirection: Axis.vertical,
children:
snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return StreamBuilder<DocumentSnapshot>(
stream: users
.doc(data['Uid'])
.get()
.asStream()
.asBroadcastStream(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {}
if (!(snapshot.hasData)) {
print("no data");
return SizedBox(
width: 0,
);
}
if (snapshot.connectionState ==
ConnectionState.done) {
Map<String, dynamic> daata = snapshot.data!
.data() as Map<String, dynamic>;
String username = daata['Username'];
String userimage = daata['Userimage'];
return mypost(
context,
data['title'],
data['ImageUrl'],
data['context'],
username,
userimage,
data['nlikes'],
data['ncomments'],
data['date']
.toDate()
.toString()
.split(' ')
.first);
}
return const Text("loading");
});
}).toList(),
),
);
}),
if any could help I would be happy with that.
It might interest you to know that when I run the below code (basically your code, but with my streams and mypost() function) I don't get any errors!... It scrolls fine!
import 'package:firebase_core/firebase_core.dart';
import 'package:my_app/firebase_labels.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Screen(),
);
}
}
class MyFirebase {
static FirebaseFirestore storeObject = FirebaseFirestore.instance;
}
class Screen extends StatelessWidget {
Screen({Key? key}) : super(key: key);
// Let me just define some streams here, from the same CollectionReference:
final CollectionReference posts = MyFirebase.storeObject
.collection(kCollectionConversations);
final CollectionReference users = MyFirebase.storeObject
.collection(kCollectionConversations);
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: posts.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
return SizedBox(
height: MediaQuery.of(context).size.height * 0.69,
child: ListView(
scrollDirection: Axis.vertical,
children:
snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return StreamBuilder<DocumentSnapshot>(
stream: users
.doc(data['Uid'])
.get()
.asStream()
.asBroadcastStream(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {}
if (!(snapshot.hasData)) {
print("no data");
return SizedBox(
width: 0,
);
}
if (snapshot.connectionState ==
ConnectionState.done) {
Map<String, dynamic>? daata = snapshot.data!
.data() as Map<String, dynamic>?;
String username = '';
String userimage = '';
if (daata != null) {
username = daata['Username'];
userimage = daata['Userimage'];
}
return mypost(
data,
// context,
data['title'],
data['ImageUrl'],
data['context'],
username,
userimage,
data['nlikes'],
data['ncomments'],
// data['date']
// .toDate()
// .toString()
// .split(' ')
// .first
);
}
return const Text("loading");
});
}
).toList(),
),
);
}),
);
}
}
Widget mypost(var data1, var data2, var data3, var data4, var data5, var data6, var data7, var data8/*, var data9,*/) {
return Container(
// height: 50,
child: Text('$data1'),
decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
);
}
If you copy-paste this code into yours, do you get errors?
What if you change the streams for yours and the mypost() for yours? Do you get errors then?

I'm getting a blank screen instead of firestore data

my main code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:day_event_viewer/services/firestore.dart';
import 'package:flutter/material.dart';
class TodayEventScreen extends StatefulWidget {
#override
_TodayEventScreenState createState() => _TodayEventScreenState();
}
class _TodayEventScreenState extends State<TodayEventScreen> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: StreamBuilder(
stream: getUserDatas(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return const Text('loading data');
} else if (snapshot.hasData) {
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['name']),
// subtitle: Text(data['company']),
);
}).toList(),
);
}
return const Text('somethng\'s wrong');
}),
);
}
}
the stream getUserDatas() mentioned above:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:day_event_viewer/screens/add_screen.dart';
Stream<QuerySnapshot> getUserDatas() async* {
final uid = await getUid();
yield* FirebaseFirestore.instance
.collection('usersdatas')
.doc(uid)
.collection('profile')
.where('date', isEqualTo: DateTime.now().day)
.snapshots();
}
when I comment the code ".where('date', isEqualTo: DateTime.now().day)" 'above 4 lines' it's working fine so i think maybe my querying is the problem but i don't know how to fix it.
try this
title: Text(document.get('name')),
you should put else keyword before this line.
return const Text('somethng\'s wrong');

Error: A value of type 'Future<ListView>' can't be returned from a function with return type 'Widget'

I'm tryng to read data from Cloud Firestore (working) and put the data in a ListView containing a graphic widget (Order) that shows price, date and product, but when I'm trying to return the data from the method this error shows up.
The target is to return the data of the user passed to the db_utility constructor.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'order.dart';
class db_utility extends StatelessWidget {
final String userID;
const db_utility(this.userID, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder(
future: users.doc(userID).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text("error");
} else if (snapshot.hasData && !snapshot.data!.exists) {
return const Text("no data found");
} else if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return getListOfOrders();
}
return const Center(
child: CircularProgressIndicator(
color: Colors.red,
),
);
},
);
}
And this is the method that returns the ListView:
Future<ListView> getListOfOrders() async {
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('completed_orders')
.where('user', isEqualTo: userID)
.orderBy('timestamp')
.get();
final data = snapshot.docs.map((doc) => doc.data()).toList();
List<Order> orders = <Order>[];
for (var o in data) {
orders.add(Order((o as Map)['price'], 'date of today', o['product']));
}
return ListView(children: orders,);
}
}
getListOfOrders() is another future method, you can use nested FutureBuilder .
In that case, instead of return getListOfOrders(); use another FutureBuilder like previous one. But you can do the operation on single future method.
You can also check multiple method on a future builder

How to get specific fields from documentsnapshot

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.

I am failing to get data from cloud firestore while using flutter

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.