Flutter future builder displaying nothing after finishing - firestore - firebase

I'm trying to display a future builder based on firestore database, but now all I'm getting is a blank screen after finishing the circular progress indicator.
Any help would be greatly appreciated.
Firestore function :
Future<List<Item>> getFavourites() async{
List<Item> _itemList= [];
var firebaseUser=await FirebaseAuth.instance.currentUser();
Firestore.instance.collection("users").document(firebaseUser.uid).get().then((querySnapshot){
List value = querySnapshot.data["favourites"];
if(value.length>0){
value.forEach((element) {
Firestore.instance.collection("items").document(element).get().then((value){
Item item= Item.fromMap(value.data);
_itemList.add(item);
});
});
}
});
return _itemList;
}
FutureBuilder :
FutureBuilder(
future: getFavourites(),
builder:(BuildContext context,AsyncSnapshot<List<Item>> snapshot){
if(snapshot.connectionState!=ConnectionState.waiting){
//print(snapshot.data[0].name);
return
ListView(
children: <Widget>[
SizedBox(height: 10.0),
Text(
"Favorites",
style: TextStyle(
fontSize: 23,
),
),
SizedBox(height: 10.0),
GridView.builder(
shrinkWrap: true,
primary: false,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: MediaQuery.of(context).size.width /
(MediaQuery.of(context).size.height / 1.25),
),
itemCount: snapshot.data == null ? 0 :snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return GridItem(
img: snapshot.data[index].img,
name: snapshot.data[index].name,
category: snapshot.data[index].category,
id: snapshot.data[index].id,
);
},
),
SizedBox(height: 30),
],
);
}
else{
return Center(
child: CircularProgressIndicator(),
);
}
}
),
'Item' is a class containing all the variables as in the firestore collection.

Use the following method:
Future<List<Item>> getFavourites() async{
List<Item> _itemList= [];
var firebaseUser= await FirebaseAuth.instance.currentUser();
DocumentSnapshot snapshot = await Firestore.instance.collection("users").document(firebaseUser.uid).get();
List value = snapshot.data["favourites"];
if(value.length>0){
value.forEach((element) async{
DocumentSnapshot docSnapshot = await Firestore.instance.collection("items").document(element).get();
Item item= Item.fromMap(docSnapshot.data);
_itemList.add(item);
});
}
return _itemList;
}
In the FutureBuilder use :
if(snapshot.connectionState==ConnectionState.done){

Have you tried to check for:
if(snapshot.connectionState==ConnectionState.done){
instead of
if(snapshot.connectionState!=ConnectionState.waiting){
?

Related

type '_AssertionError' is not a subtype of type 'String'

I've been dealing with this problem for a long time. I am trying to parse a set of items underloved items for each user (Uid). I am getting the response body and it is been converted to list.But I am getting this error:
======== Exception caught by widgets library =======================================================
The following _TypeError was thrown building FutureBuilder<dynamic>(dirty, state: _FutureBuilderState<dynamic>#7125a):
type '_AssertionError' is not a subtype of type 'String'
The relevant error-causing widget was:
FutureBuilder<dynamic> file:///C:/Users/arunb/AndroidStudioProjects/resplash/lib/pages/bookmark.dart:35:15
When the exception was thrown, this was the stack:
#0 BookmarkPage.build.<anonymous closure> (package:resplash/pages/bookmark.dart:47:44)
#1 _FutureBuilderState.build (package:flutter/src/widgets/async.dart:775:55)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4691:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4574:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4746:11)
...
Codes :
bookmark.dart
class BookmarkPage extends StatelessWidget {
const BookmarkPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final sb = context.watch<SignInBloc>();
return RefreshIndicator(
onRefresh: () async {
await context.read<BookmarkBloc>().getData();
},
child: Scaffold(
backgroundColor: Theme.of(context).primaryColor,
appBar: AppBar(
backgroundColor: Theme.of(context).primaryColor,
centerTitle: false,
title: Text('Saved Items'),
),
body: sb.guestUser == true
? EmptyPage(
icon: FontAwesomeIcons.heart,
title: 'No wallpapers found.\n Sign in to access this feature',
)
: FutureBuilder(
future: context.watch<BookmarkBloc>().getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
if (snapshot.data.length == 0)
return EmptyPage(
icon: FontAwesomeIcons.heart,
title: 'No wallpapers found',
);
return _buildList(snapshot);
} else if (snapshot.hasError) {
return Center(
child: Text(snapshot.error),
);
}
return Center(
child: CupertinoActivityIndicator(),
);
},
),
),
);
}
Widget _buildList(snapshot) {
return StaggeredGridView.countBuilder(
crossAxisCount: 4,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
List d = snapshot.data;
return InkWell(
child: Stack(
children: <Widget>[
Hero(
tag: 'bookmark$index',
child: cachedImage(d[index]['image url'])),
Positioned(
bottom: 15,
left: 12,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
d[index]['category'],
style: TextStyle(color: Colors.white, fontSize: 18),
)
],
),
),
Positioned(
right: 10,
top: 20,
child: Row(
children: [
Icon(Icons.favorite,
color: Colors.white.withOpacity(0.5), size: 25),
Text(
d[index]['loves'].toString(),
style: TextStyle(
color: Colors.white.withOpacity(0.7),
fontSize: 16,
fontWeight: FontWeight.w600),
),
],
),
),
],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsPage(
tag: 'bookmark$index',
imageUrl: d[index]['image url'],
catagory: d[index]['category'],
timestamp: d[index]['timestamp'],
)));
},
);
},
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(2, index.isEven ? 4 : 3),
mainAxisSpacing: 10,
crossAxisSpacing: 10,
padding: EdgeInsets.all(15),
);
}
}
getData() method
final FirebaseFirestore firestore = FirebaseFirestore.instance;
getData() async {
SharedPreferences sp = await SharedPreferences.getInstance();
String _uid = sp.getString('uid');
final DocumentReference ref = firestore.collection('users').doc(_uid);
DocumentSnapshot snap = await ref.get();
List d = snap['loved items'];
List filteredData = [];
if (d.isNotEmpty) {
await firestore
.collection('contents')
.where('timestamp', whereIn: d)
.get()
.then((QuerySnapshot snap) {
filteredData = snap.docs;
});
}
notifyListeners();
return filteredData;
}
How can I solve this problem? I don't exactly know what is the reason and how to solve this problem.
Can't say for sure just based on this info, but my best guess is that
firestore
.collection('contents')
.where('timestamp', whereIn: d)
.get()
is returning an error.
But I would advise to take full advantage of await since you are using it. Also add a catch on the get() method. So do something like:
if (d.isNotEmpty) {
try {
const snapshot:QuerySnapshot = await firestore
.collection('contents')
.where('timestamp', whereIn: d)
.get();
(*)
fiteredData = snapshot.docs
}
catch(e) {
console.log(e)
// or/and error handling
}
}
(*) you can also try to log the snapshot at the asterix position in the code ((*)) to try to track down the error (if you think there is any chance that the assign is causing problems)
I am not sure if this fix your issue (without a reproducible example is pretty hard to know exactly what is going wrong) but it should at the very least help you to track down the problem.

How can I change a Futurebuilder into an Streambuilder?

I'm using a future builder in a method and trying to switch to a Streambuilder but struggling a it with that heres my code may be anyone can help
class _MeineFreundeState extends State<MeineFreunde> {
Icon custIcon = Icon(Icons.search);
Widget cusSearchBar = Text("Meine Freunde");
Stream myVideos;
int likes = 0;
int videos = 0;
int followers;
int following;
bool dataisthere = false;
#override
void initState() {
super.initState();
getalldata();
}
getalldata() async {
var listOfIds = [];
String myID = FirebaseAuth.instance.currentUser.uid;
var idofotheruser = await FirebaseFirestore.instance
.collection('meinprofilsettings')
.doc(myID)
.collection('following')
.get();
following = idofotheruser.docs.length;
idofotheruser.docs.forEach((element) {
listOfIds.add(element.id);
});
print(listOfIds);
myVideos = FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: 'Fp3unLwcl2SGVh4MbUPiRVAylYV2')
.snapshots();
var documents = await FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: 'Fp3unLwcl2SGVh4MbUPiRVAylYV2')
.get();
if (!mounted) return;
setState(() {
videos = documents.docs.length;
});
for (var item in documents.docs) {
likes = item.data()['likes'].length + likes;
}
var followersdocuments = await FirebaseFirestore.instance
.collection("meinprofilsettings")
.doc(myID)
.collection('followers')
.get();
var followingdocuments = await FirebaseFirestore.instance
.collection("meinprofilsettings")
.doc(myID)
.collection('following')
.get();
followers = followersdocuments.docs.length;
following = followingdocuments.docs.length;
setState(() {
dataisthere = true;
});
}
#override
Widget build(BuildContext context) {
return getBody(context);
}
Widget getBody(BuildContext context) {
return dataisthere == false
? Scaffold(body: Center(child: CircularProgressIndicator()))
: Stack(children: <Widget>[
Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
Navigator.of(context)
.pushNamed(Searchuserinmeinebeitraege.route);
},
),
],
backgroundColor: Colors.transparent,
elevation: 0.0,
),
body: RefreshIndicator(
onRefresh: _handleRefresh,
color: Colors.black,
strokeWidth: 4,
child: ListView(
children: [
Column(children: <Widget>[
SizedBox(
height: 5,
),
StreamBuilder(
stream: myVideos,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (videos > 0) {
return StaggeredGridView.countBuilder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
crossAxisCount: 3,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot video =
snapshot.data.docs[index];
return InkWell(
onTap: () {
NavigationService.instance
.navigateToRoute(MaterialPageRoute(
builder: (context) {
return VideoPage(
video.data()['videourl'],
video.data()['uid'],
video.id,
);
}));
},
child: Card(
elevation: 0.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
clipBehavior:
Clip.antiAliasWithSaveLayer,
child: Image.network(
video.data()['previewimage'],
fit: BoxFit.cover,
),
),
//imageData: searchImages[index],
),
);
},
staggeredTileBuilder: (index) =>
StaggeredTile.count(
(index % 7 == 0) ? 2 : 1,
(index % 7 == 0) ? 2 : 1),
mainAxisSpacing: 8.0,
crossAxisSpacing: 4.0,
);
} else {
return Center(
child: Padding(
padding:
const EdgeInsets.fromLTRB(0, 100, 0, 0),
child: Container(
child: Text(
"No Videos Yet",
style: TextStyle(
fontSize: 18, color: Colors.black),
),
),
),
);
}
}),
]),
],
),
),
),
]);
}
Future _handleRefresh() async {
await Future.delayed(new Duration(seconds: 2));
setState(() {
getalldata();
});
return null;
}
}
I am a beginner with flutter, I know that I just can change FuturBuilder into Streambuilder and then future to stream but what about How I'm getting the data is there any difference
I Mean something like this line
video.data()['videourl'],
Is it equal or is there any difference and also how can I change it in getalldata method. If you need more information please leave a comment.
StreamBuilder is different from FutureBuilder in many ways one main difference being
The main job of the FutureBuilder is to complete the future and return the result once the result is returned it has no way to fetch the latest snapshot from the future unless its parent rebuilds. Once the future attached returns the result the builder method gets executed to refresh the Ui.
while incase of StreamBuilder it contiuously listens to your specified collection and gets you the latest snapshot in realtime. that means any document in your firestore collection changes you get the latest updated collection and builder method rebuilds to refresh the UI.
You could use StreamBuilder to fetch data from your firestore's collection like this
String myID = FirebaseAuth.instance.currentUser.uid;
final queryVideos = await FirebaseFirestore.instance
.collection('videos')
.where('uid', arrayContains: listOfIds)
StreamBuilder<DocumentSnapshot>(
stream: queryVideos.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator()); /// show a loader
} else if (snapshot.data.docs.isEmpty) {
return const SizedBox.shrink(); // show an empty widget no docs
} else
return StaggeredGridView.countBuilder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
crossAxisCount: 3,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
/// fetch a doc by Index
final doc = snapshot.data.docs[index];
return InkWell(
onTap: () {
NavigationService.instance
.navigateToRoute(MaterialPageRoute(
builder: (context)=>VideoPage(
doc['videourl'], // this is how you access each property in a document
doc['uid'],
doc['id']
));
},
child: YourWidget());
}));
});

How can I check if a Documentsnapshot has data?

Im trying to figuring out if videos exists inside a collection or not and if they exist I wanna show them and showing them works actually but if not then I wanna print a default text like No videos yet but I dont know how I can do this . Maybe anyone can help.
Heres my code in the Inkwell I displaying all videos
FutureBuilder(
future: myVideos,
builder: (BuildContext context, snapshot) {
if (!snapshot.hasData) {
return Container();
}
return StaggeredGridView.countBuilder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
crossAxisCount: 3,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot video =
snapshot.data.docs[index];
return InkWell(
onTap: (){
Navigator.of(context).pushNamed(
ChatFirstpage.route);
},child: Card(
elevation: 0.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Image.network(
video.data()['previewimage'],
fit: BoxFit.cover,
),
),
//imageData: searchImages[index],
),
);
},
staggeredTileBuilder: (index) =>
StaggeredTile.count((index % 7 == 0) ? 2 : 1,
(index % 7 == 0) ? 2 : 1),
mainAxisSpacing: 8.0,
crossAxisSpacing: 4.0,
);
},
),
And heres my Videos collection
Future myVideos;
int likes = 0;
bool dataisthere = false;
#override
void initState() {
super.initState();
getalldata();
}
getalldata() async {
//get videos as future
myVideos = FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: widget.userid)
.get();
var documents = await FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: widget.userid)
.get();
for (var item in documents.docs) {
likes = item.data()['likes'].length + likes;
}
setState(() {
dataisthere = true;
});
}
You can do a check before navigating to the movie's page. Something like:
// ... other lines
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot video = snapshot.data.docs[index];
return InkWell(
onTap: () {
// Do your check here, if video not exist display a SnackBar
if (video.data()['video_url'] == null) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Video not exist'),
duration: Duration(seconds: 2),
backgroundColor: Colors.orange,
));
} else {
// If video exist, navigate to the page
Navigator.of(context).pushNamed(ChatFirstpage.route);
}
},
child: Card(
elevation: 0.0,
child: ClipRRect(
// ... other lines
Try this:
DocumentSnapshot video = snapshot.data.docs[index];
if (video.data()['previewimage'] == null) {return Text('no video');}

How to display the data from firebase in flutter?

I am able to retrieve data from Firebase in flutter, but I do not know how to display them in my application as a list. Also, I do not know where to write my codes, do I have to write them in init state method or some where else?
I am able only to print the values in the debug console, not in the app. Please find the following codes that I am using to retrieve data from firebase and print them in debug console. These codes are written in the main.dart inside the initState method.
final retrieve = FirebaseDatabase.instance.reference().child("Transaction");
String _titleController;
String _amountController;
String _selectedDate;
String _selectedpicker;
#override
void initState() {
retrieve.once().then(
(DataSnapshot snapshot) {
Map<dynamic, dynamic> values = snapshot.value;
//for loop
values.forEach(
(key, value) {
print("OOoooooo");
print(value['title']);
final strem =
Firestore.instance.collection('Transaction').snapshots(),
_titleController = value['title'];
_amountController = value['amount'];
_selectedDate = value['Picker'];
_selectedpicker = value['Date'];
return StreamBuilder(
stream: stream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("Loading");
} else {
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot mytransaction =
snapshot.data.documents[index];
return Card(
elevation: 5,
margin: EdgeInsets.symmetric(
vertical: 8, horizontal: 5),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.purple,
radius: 30,
child: Padding(
padding: EdgeInsets.all(6),
child: FittedBox(
child: Text(
'\$${mytransaction['amount']}',
style: TextStyle(
color: Colors.white,
fontFamily: 'FjallaOne'),
),
),
),
),
title: Text(
'${mytransaction['title']}' +
" " +
'${mytransaction['Picker']}',
style: TextStyle(
color: Colors.black,
fontFamily: 'FjallaOne'),
),
subtitle: Text(
'${mytransaction['Date']}',
),
));
});
}
});
},
);
},
);
}
You need a list view builder or grid view builder or any builder depending on how you want to show your data.
Example:
return ListView.builder
(itemCount: litems.length,
itemBuilder: (BuildContext ctxt, int index) {
return new Text(value['title]);
}
)
You can return a column or something else to display all your data

Flutter: Unable to read data from firebase. The method '[]' was called on null error

I am trying to read the data from firebase after scanning a barcode.
This is how it should appear, but instead of barcode, it should display name and price from the database
(https://m.imgur.com/gallery/lEFJZ0Q)
Code:
class ListTileModel {
String barcode;
ListTileModel(this.barcode);
}
the below code is inside the stateful widget
List<ListTileModel> _items = [];
String barcode = "";
void _add() {
_items.add(ListTileModel(barcode));
setState(() {});
}
#override
void initState(){
super.initState();
}
StreamBuiler Used:
new Container(
child: ListView(
children: _items
.map((item) => StreamBuilder(
stream: FirebaseDatabase.instance.reference().child('prd').child(item.barcode).onValue,
builder: (context, snap) {
print(item.barcode);
if(snap.hasData){
DataSnapshot snapshot = snap.data.snapshot;
Map<dynamic, dynamic> itm = snapshot.value;
return snap.data.snapshot.value == null
? SizedBox()
: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: 1,
itemBuilder: (context, index) {
return Row(
children: <Widget>[
ConstrainedBox(
child: Container(
child: Text(itm[index]['name'],style: TextStyle(fontSize: 20),),
),
),
SizedBox(width: 100,),
ConstrainedBox(
child: Container(
child: Text(itm[index]['price'].toString(),style: TextStyle(fontSize: 20),),
),
),
],
);
},
);
}
else {
return Center(child: CircularProgressIndicator());
}
}
),
).toList()
),
),
The Barcode Scan code:
widget:
floatingActionButton: FloatingActionButton(
onPressed: scan,
child: Icon(Icons.add_shopping_cart),
),
scan() :
Future scan() async{
try{
String barcode = await BarcodeScanner.scan();
setState(() {
this.barcode = barcode;
});
_add();
}on PlatformException catch(e) {
if(e.code == BarcodeScanner.CameraAccessDenied){
setState(() {
this.barcode = 'Access Denied';
});
}
}
I'm am getting following Error:
The following NoSuchMethodError was thrown building:
The method '[]' was called on null.
Receiver: null
Tried calling:
Please try this and let me know if it fixes your problem you need to change
Text(itm[index]['name'],style: TextStyle(fontSize: 20),),
Text(itm[index]['price'].toString(),style: TextStyle(fontSize: 20),),
To
Text(itm['det']['name'],style: TextStyle(fontSize: 20),),
Text(itm['det']['price'].toString(),style: TextStyle(fontSize: 20),),
Let me know if this works for you. I believe the problem is the index also.
right now your saying.

Resources