How to show a text widget when a screen is not calling data from Firebase - firebase

I have the activity screen that gets data from Firebase, I'm trying to show a text message like "No activities yet" when the database is not providing data.
I tried to use a ternary operator condition that checks if the database is empty and it works, however, the problem is that when I go the activity screen and it has data, it still shows the message for about 1 second and then shows the activity screen, so, I would like to know if there is a better way to implement this.
Hope this makes sense.
See the code below
#override
Widget build(BuildContext context) {
return Scaffold(
body: RefreshIndicator(
onRefresh: () => _setupActivities(),
child: _activities.isEmpty
? Center(
child: Text(
'Aun no Tienes Notificaciones',
style: ktextTitlesStyle.copyWith(
color: Colors.black,
fontSize: responsive.height(context) / 50),
),
)
: ListView.builder(
itemCount: _activities.length,
itemBuilder: (BuildContext context, int index) {
Activity activity = _activities[index];
return _buildActivity(activity);
},
),
),
);
}
}

I'm not sure I fully understand what you are trying to accomplish but checking for .hasData on the AsyncSnapshot might help determine if data is returned.
Container( child: FutureBuilder(future: someFutureFirebaseDataReturningMethod(), builder: (context, snapshot) {
if (!snapshot.hasData) return _noActiviesYet();
return _showActivies(snapshot, context);
}))
.hasData returns "whether this snapshot contains a non-null data value."(https://api.flutter.dev/flutter/widgets/AsyncSnapshot/hasData.html)

Related

Firebase doesn't work cause of null-safety (DART/FLUTTER)

I'm using/learning Firebase for my database works. My snapshot's coming like _jsonQuerySnapshot or _jsonDocumentSnapshot. But it had to be QuerySnapshot or DocumentSnapshot. Because of this I have to encode and decode my snapshot for use my datas.
If I'm not using encode decode json I'm getting null or object errors all the time.
Here is my class extends from state
class _MyHomePageState extends State<MyHomePage> {
final _firestore = FirebaseFirestore.instance;
#override
Widget build(BuildContext context) {
CollectionReference moviesRef=_firestore.collection('movies');
DocumentReference babaRef = _firestore.collection('movies').doc('Baba');
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: Text('FireStore Crud'),
),
body: Center(
child: Container(
child: Column(
children: [
StreamBuilder<QuerySnapshot>(
stream: moviesRef.snapshots(),
builder: (BuildContext context,AsyncSnapshot asyncSnapshot){
List<DocumentSnapshot>listOfDocumentSnapshot=asyncSnapshot.data.docs;
return Flexible(
child: ListView.builder(
itemCount: listOfDocumentSnapshot.length,
itemBuilder: (context,index){
Text('${listOfDocumentSnapshot[index].data()['name']}' ,style: TextStyle(fontSize: 24),);
},
),
);
},
),
],
),
),
),
);
}
}
and this is my error .
First of all, check your data is null or not and then use [] on it. Probably, listOfDocumentSnapshot[index].data() is null. If it is null, render another UI such as loading screen. Namely, your loading screen must be showed until reach the data.
for example:
builder: (BuildContext context,AsyncSnapshot asyncSnapshot){
List<DocumentSnapshot>? listOfDocumentSnapshot = asyncSnapshot.data.docs;
if(!listOfDocumentSnapshot.hasData || listOfDocumentSnapshot == null){
return LoadingScreen(); //etc.
}
return Flexible(
child: ListView.builder(
itemCount: listOfDocumentSnapshot.length,
itemBuilder: (context,index){
Text('${listOfDocumentSnapshot[index].data()['name']}' ,style: TextStyle(fontSize: 24),);
},
),
);
},
Futures (asynchronous programmes) need some time to get data and you have to make your UI wait until you get your data. e.g. database connections, read/write somethings to/from somewhere etc.
For more detail you can read this article.

StreamBuilder with flutter_firebase has null value on second widget build (image disappears)

I managed to show images from firestore using streambuilder on a page, but the problem is that the image disappears (I get a null snapshot.data value) if I go back to the last page and come back.
Here is my code. How can I make the snapshot data persist, so the image stays there and doesn't disappear when the widget rebuilds?
Container(child: Column(
children: [
Text(' Certifications',
Container(child: StreamBuilder(
stream: certificates,
builder: (context, snapshot) {
return !snapshot.hasData
? Center(child: Container(
child: Center(child: Text(
'No images yet'))))
: Container(
child: GridView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
url = snapshot.data.documents[index].get('url');
return Container(child: FadeInImage
.memoryNetwork(fit: BoxFit.cover,
placeholder: kTransparentImage,
image: url),
),
);
}),
);
}),
),
Streams are asynchronous. which means StreamBuilder does not get old snapshots, only new future snapshots. This is your problem.
When your widget is re-built it is subscribing to a stream that has already had events. Yes you would think that data in the snapshot should be the last event value, but that is not the case. It will be null until a new event is pushed onto your certificates stream.
So one solution is for the service that is loading your certificates to store the value of the initial API request and make that available for you to use in your StreamBuilder's initialData property.
I would structure it like this:
StreamBuilder(
stream: certificateService.stream,
initialData: certificateService.value, // <-----
builder: ...
)
Hope this points you in the right direction.

How to display any Data from Firestore using ternary operator

I was trying to fetch data from firebase and I wanted to show an Icon of Instagram only if there is any data available in the firebase collection. I used the following method to fetch the data from firebase
StreamBuilder(
stream: Firestore.instance.collection("aboutpage").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
child: Center(
child: SpinKitThreeBounce(
color: Colors.lightBlueAccent,
size: 30.0,
),
),
);
} else {
return Container(
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
DocumentSnapshot aboutpage = snapshot.data.documents[index];
return Column(
There is no Problem while retrieving data from firebase. Probleme is when I want to display an Instagram icon if the data is available in firebase is used ternary operator but did not work for me
"${aboutpage["name"]}" == null
? Container()
: IconButton(Icon: Icons.instagram),
onPressed: (}{}
),
I also tried using "${aboutpage["name"]}".isEmpty but dint work for me. So, How can I fetch data only if its available and if empty return empty Container?
Change the condition from "${aboutpage["name"]}" == null to aboutpage is Map && aboutpage["name"] == null.

How to check if a document is empty

I have the following empty document:
As I understand it, the document exists, but it's empty.
I'm showing the user a list of all document ID, then allowing him to choose the document to display data for.
When the user chooses this empty document, an error appears because i called length on null object in my listview. That's fair.
I want to show the user an alert dialog informing him that it's empty.
child: FutureBuilder(
future: snapshot,
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasData) {
if(snapshot.data.data.length == 0) {
return AlertDialog(
content: Text('Wrong Date', textAlign: TextAlign.center, style: TextStyle(color: Colors.black)),
backgroundColor: Colors.redAccent,
actions: [Icon(Icons.keyboard_backspace)],
);
}
This is what I'm going with at the moment. Just checking the length of the document.
That does not seem right to me.
Is there any other way of doing this?
Looking at the API for DocumentSnapshot, the data() method returns a Map of fields. If you want to know if there are no fields, simply ask the Map if its length is 0.
snapshot.data().length == 0

Flutter Firebase ListView - Slow Refreshes

I created a ListView that populates from a Firebase collection by using a StreamBuilder widget. It takes some time for the ListView to populate because I'm running tasks (HTTP requests) for each item of the Firebase collection and then displaying the result in the list.
When I navigate away from the page with the ListView and then return to the page (using PageView), the ListView appears to refresh entirely instead of using the last seen version. So there is a ~5 second circular progress indicator while the list re-populates every time the page is re-opened.
Questions:
What is the best way to make this ListView not complete a full 5
second refresh every time the page is re-opened? Can it use the last seen version and only update when items are added to the firebase collection?
If I were to remove the tasks (HTTP requests) that need to be ran on each item of the collection and instead simply show values directly from the Firebase collection, should the refresh time be fast enough that it is not a problem?
Is it best to create a local database (using sqflite) that syncs with the Firebase collection to prevent slow refreshes?
Code:
class AccountsPage extends StatefulWidget {
#override
_AccountsPageState createState() => _AccountsPageState();
}
class _AccountsPageState extends State<AccountsPage> {
User user;
Widget _buildListItem(BuildContext context, DocumentSnapshot document, String uuid) {
// get data from firebase
String token = document.data.values.toList()[0];
// For current document/token, make an HTTP request using the token and return relevant data
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: FutureBuilder(
future: anHTTPrequest(token, uuid),
builder: (context, projectSnap) {
if (projectSnap.connectionState == ConnectionState.none ||
!projectSnap.hasData || projectSnap.data.length == 0) {
return Container();
}
return ListView.builder(
shrinkWrap: true,
itemCount: projectSnap.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(projectSnap.data[index]),
);
},
);
},
),
)],
);
}
#override
Widget build(BuildContext context) {
final container = StateContainer.of(context);
user = container.user;
return Container(
child: Scaffold(
body: Column(
children: <Widget>[
new Flexible(
child: StreamBuilder(
stream: Provider.of(context).collectionRef.document(user.uuid).collection('tokens').snapshots(),
builder: (context, snapshot){
if (!snapshot.hasData){
return Container(
child: Center(
child: Text("No data")
)
);
}
return ListView.builder(
padding: EdgeInsets.all(8.0),
reverse: false,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, int index) {
return _buildListItem(context, snapshot.data.documents[index], user.uuid);
}
);
}
)
),
]
),
),
);
}
}

Resources