Future Builder don't show data from Cloud Firestore - firebase

I load data from Cloud Firestore from Firebase. In the print statement in the function loadAssignments() the loaded data are showing correct. But when i will show the data in the Text widget with the widget FutureBuilder it doesn't show the data and i don't know why. Can anyone please help me?
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AssignmentPage extends StatefulWidget {
#override
_AssignmentPageState createState() => _AssignmentPageState();
}
class _AssignmentPageState extends State<AssignmentPage> {
Future _loadAssignments;
QuerySnapshot _assignments;
#override
void initState() {
super.initState();
_loadAssignments = loadAssignments();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bühlmaier App'),
centerTitle: true,
automaticallyImplyLeading: false,
),
body: FutureBuilder(
future: _loadAssignments,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
itemCount: _assignments.documents.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Text('${_assignments.documents[index].data['Name'].toString()}'),
),
);
});
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
return Center(child: CircularProgressIndicator());
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => toPage(NewAssignmentPage()),
),
);
}
Future<void> loadAssignments() async {
_assignments = await Firestore.instance.collection('assignments').getDocuments();
for (int i = 0; i < _assignments.documents.length; i++) {
print('DATEN: ' + _assignments.documents[i].data['Name'].toString());
}
setState(() {});
}
}
When i add the following code:
else if (snapshot.connectionState == ConnectionState.waiting) {
return Text("No data");
}
it works, but when i edit the code to:
else if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
it don't load the data and the CircularProgressIndicator() is not moving.

Use the method in the future property:
FutureBuilder(
future: loadAssignments(),
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 Card(
child: ListTile(
leading: Text('${snapshot.data.documents[index].data['Name'].toString()}'),
),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
),
Change the method to the following:
Future<QuerySnapshot> loadAssignments() async {
return await Firestore.instance.collection('assignments').getDocuments();
}

Related

How to use get firebase data to StreamBuilder without Listview

I need to show my firebase data in deference places. But in the same page.
In every tutorial I followed they use Listview and I need to know there is another way to get data without using the list view
Ya finally I think I found an answer
final uid = FirebaseAuth.instance.currentUser!.uid;
userdata = FirebaseFirestore.instance.collection('/users/$uid/userdata');
return Scaffold(
body: StreamBuilder(
stream: userdata.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
try {
snapshot.data!.docs.map((DocumentSnapshot document) {
data = document.data()! as Map<String, dynamic>;
}).toList();
} catch (e) {
print(e.toString());
}
},
),
);
Expanded(
child: StreamBuilder(
stream:
FirebaseFirestore.instance.collection("posts").snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>>
snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
log(snapshot.data!.docs[index].data().toString());
return UserVideos(
snap: snapshot.data!.docs[index].data(),
);
},
);
}
log("${snapshot.stackTrace}stack trace");
log("${snapshot.error}error");
return const Text("Something went wrong");
},
),
),

Firebase flutter _CastError (Null check operator used on a null value)

The documents on the tax page do not appear on the screen. I am getting the following _CastError (Null check operator used on a null value). Is there anyone who can help?
firestore_db_services.dart
#override
Stream<List<Vergi>> getHizmetler(String currentUserID) {
var snapShot = _firebaseFirestoreDB
.collection("hizmetler")
.doc(currentUserID)
.collection("vergi")
.orderBy("aciklamaDate")
.snapshots();
return snapShot.map(
(vergiListesi) => vergiListesi.docs
.map(
(vergi) => Vergi.fromMap(vergi.data()),
)
.toList(),
);
}
vergi.dart
Expanded(
child: StreamBuilder<List<Vergi>?>(
stream: _userModel.getHizmetler(_currentUser.userID),
builder: (context, snapShot) {
if (snapShot.hasError) {
return Center(
child: MyIndicator(),
);
}
if (snapShot.connectionState == ConnectionState.waiting) {
return Text("Yükleniyor...");
}
List<Vergi>? tumVergiListesi = snapShot.data;
return ListView.builder(
itemCount: tumVergiListesi!.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
tumVergiListesi[index].aciklama.toString(),
),
subtitle: Text(
tumVergiListesi[index].fiyat.toString(),
),
);
},
);
},
),
),
Try returning snapshots from getHizmetler function and not the list you are currently returning:
Stream<QuerySnapshot>getHizmetler(String currentUserID) {
return _firebaseFirestoreDB
.collection("hizmetler")
.doc(currentUserID)
.collection("vergi")
.orderBy("aciklamaDate")
.snapshots();
}
Adjust your StreamBuilder like:
StreamBuilder<QuerySnapshot>(
stream: _userModel.getHizmetler(_currentUser.userID),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
// error handling
return ...;
}
if (snapshot.connectionState == ConnectionState.waiting) {
// progress indicator
return ...;
}
if (snapshot.hasData) {
// here you can do the conversion you like, result will be in:
// snapshot.data!.docs
}
}
)

Firestore <QuerySnapshot>

I have a problem with my class GetInfo.
There is an error with .
The name 'QuerySnapshot' is defined in the libraries 'package:cloud_firestore/cloud_firestore.dart' and 'package:firebase/src/firestore.dart (via package:firebase/firestore.dart)'.
Try using 'as prefix' for one of the import directives, or hiding the name from all but one of the imports.
class GetInfo extends StatelessWidget {
const GetInfo({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('stories').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('Loading');
}
return new ListView(
children: snapshot.data!.docs.map((document) {
return new ListTile(
title: Text(document.get('display_name')),
subtitle: Text(document.get('profession')),
);
}).toList(),
);
}),
);
}
}
I am using Flatter with Firebase, Firestore. I am trying to follow one of the courses but it seems this is based on old Firebase Version and I don't know how to fix the code. Any advice? Thanks for your answers!
the package is updated please check the example.
body: StreamBuilder<QuerySnapshot<Movie>>(
stream: moviesRef.queryBy(query).snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
}
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final data = snapshot.requireData;
return ListView.builder(
itemCount: data.size,
itemBuilder: (context, index) {
return _MovieItem(
data.docs[index].data(),
data.docs[index].reference,
);
},
);
},
),
final moviesRef = FirebaseFirestore.instance
.collection('firestore-example-app')
.withConverter<Movie>(
fromFirestore: (snapshots, _) => Movie.fromJson(snapshots.data()!),
toFirestore: (movie, _) => movie.toJson(),
);

Get data from Cloud firestore and displays them into widget

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();
},
),

Trying to implement loading spinner while loading data from Firestore with Flutter

I'm working on an app that display spinner when backend loading data from Firestore but it's not worked as intended and I'm having a struggle to find the flaw.
My Orders Page code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:table_service/providers/order.dart';
import '../providers/session.dart';
class OrdersPage extends StatefulWidget {
bool isLoading = true;
#override
_OrdersPageState createState() => _OrdersPageState();
}
class _OrdersPageState extends State<OrdersPage> {
List<Order> _orders = [];
#override
Widget build(BuildContext context) {
final session = Provider.of<Session>(context, listen: false);
return Scaffold(
floatingActionButton: session.privilege == 'Administrator' ||
session.privilege == 'Waiter' ||
session.privilege == 'Customer'
? FloatingActionButton(
heroTag: 'OrdersPageFAB',
onPressed: () {},
child: Icon(Icons.add, color: Colors.white),
)
: null,
body: FutureBuilder(
future: session.fetchOrdersData(),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else {
print(snapshot.data);
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 2 / 2,
),
itemCount: _orders.length,
itemBuilder: (_, i) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: Card(
child: GridTile(
child: Icon(
Icons.library_books,
size: 100.0,
color: Colors.grey,
),
footer: GridTileBar(
backgroundColor: Colors.black54,
title: Text('Order by: ${_orders[i].name}'),
),
),
),
);
},
);
}
},
),
);
}
}
The fetchOrdersData() handler
final Auth auth = Auth();
final Firestore database = Firestore.instance;
String user_name;
String privilege;
List<Food> _foods = [];
List<Order> _orders = [];
List<TransactionModel.Transaction> _transactions = [];
...
...
Future fetchOrdersData() async {
_orders.clear();
return await database.collection('orders').getDocuments().then((documents) {
documents.documents.forEach((order) {
database
.collection('users')
.document(order.data['uid'])
.get()
.then((user) {
_orders.add(Order(
id: order.documentID,
tableNumber: order.data['tablenumber'],
orderDate: (order.data['orderdate'] as Timestamp).toDate(),
status: order.data['status'],
note: order.data['note'],
uid: order.data['uid'],
name: user.data['user_name'],
));
});
});
return _orders;
});
notifyListeners();
}
get getOrders {
return [..._orders];
}
I have tried many methods including StreamBuilder, setState() and recently FutureBuilder.
Did i just missing an important code?
Or did i use the wrong method?
The problem was Orders Page showing 0 data even though List on _fetchOrdersData() have 1 element.
for full source code
here on github
The other answers look reasonable. They are just missing data validation checks, which I find is required in all my apps. Because if I have a good connection, and hasData is true and hasError is false, there might be no documents at all though. This should be checked. Here is a snippet from my projects.
Checking connection state is the same as just checking snapshot.hasError.
Widget _getMyFriends() {
return StreamBuilder<QuerySnapshot>(
stream: Database.getFriendsByUserId(widget.loggedInUserId),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return Center(child: Text("Error"));
else if (!snapshot.hasData)
return Center(child: Text("Loading..."));
else if (snapshot.data.documents.isEmpty) //also check if empty! show loader?
return Center(child: Text("No friends added yet."));
else
return ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return SimpleUserPanel(userId: document['friendid']);
}).toList(),
);
}
);
}
You should do the following:
else {
if(snapshot.hasData){
print(snapshot.data);
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 2 / 2,
),
itemCount: _orders.length,
itemBuilder: (_, i) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: Card(
child: GridTile(
child: Icon(
Icons.library_books,
size: 100.0,
color: Colors.grey,
),
footer: GridTileBar(
backgroundColor: Colors.black54,
title: Text('Order by: ${_orders[i].name}'),
),
),
),
);
},
// By default, show a loading spinner.
return CircularProgressIndicator();
},
So first check if snapshot has data using the property hasData and since this is asynchronous it will first execute the return CircularProgressIndicator(); and then execute the if block.
Loking at your code, you have to check for ConnectionState.active also with your snapshot.connectionState == ConnectionState.waiting.
Actually you have more power and controll when using FutureBuilder or StreamBuilder. Below is a sample code snippet:
switch (snapshot.connectionState) {
case ConnectionState.none:
return Center(child: Text("Check Connection"));
case ConnectionState.active:
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator(backgroundColor: Theme.of(context).primaryColorLight,));
case ConnectionState.done:
if (snapshot.hasError) {
return Center(child: Text("Error occured!"));
} else if (snapshot.hasData) {
return YourWidgetWithData();
} else {
debugPrint("What went wrong");
return SizedBox();
}
break;
default:
return SizedBox();
}

Resources