nested streambuilder and snapshot in Flutter - flutter

I am wanting to use two different collections from cloud firestore so I was trying to nest stream builders. However, I cant figure out how to then nest the snapshot. The error I am currently getting is:
flutter: Another exception was thrown: type '(DocumentSnapshot) => dynamic' is not a subtype of type '(QueryDocumentSnapshot) => Widget' of 'f'
This is my code:
new StreamBuilder(
stream: FirebaseFirestore.instance
.collection('moodIcons')
.snapshots(),
builder: (context, snapshot1) {
if (!snapshot1.hasData)
return Text('Loading data... Please Wait');
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('activityIcons')
.snapshots(),
builder: (context, snapshot2) {
if (!snapshot2.hasData)
return Text('Loading data... Please Wait');
return Container(
height: 100.0,
child: new ListView(
scrollDirection: Axis.horizontal,
children: snapshot1.data.documents
.map<Widget>(
(DocumentSnapshot document1) {
return snapshot2.data.documents
.map<Widget>(
(DocumentSnapshot document2) {
if (document1.data()['display']) {
return new Positioned(
child: new MoodButton(
onTap: () {},
iconData: IconData(
document1.data()['ref'],
fontFamily:
'MaterialIcons')));
}
if (document2.data()['display']) {
return new Positioned(
child: new MoodButton(
onTap: () {},
iconData: IconData(
document2.data()['ref'],
fontFamily:
'MaterialIcons')));
} else {
return (Container());
}
}).toList();
}).toList(),
),
);
});
}),
Any help would be appreciated!

In your code, document1 and document2 are QueryDocumentSnapshot and not DocumentSnapshot.
Also, you are nesting the maps on the moodIcons and the activityIcons.
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
final moodStream = FirebaseFirestore.instance.collection('moodIcons').snapshots();
final activityStream = FirebaseFirestore.instance.collection('activityIcons').snapshots();
return StreamBuilder<QuerySnapshot>(
stream: moodStream,
builder: (context, moodQuerySnapshot) => !moodQuerySnapshot.hasData
? Text('Loading data... Please Wait')
: StreamBuilder<QuerySnapshot>(
stream: activityStream,
builder: (context, activityQuerySnapshot) => !activityQuerySnapshot.hasData
? Text('Loading data... Please Wait')
: Container(
height: 100.0,
child: new ListView(
scrollDirection: Axis.horizontal,
children: [
...moodQuerySnapshot.data.docs,
...activityQuerySnapshot.data.docs,
]
.where((queryDocSnapshot) => queryDocSnapshot.data()['display'])
.map(
(queryDocSnapshot) => Positioned(
child: MoodButton(
onTap: () {},
iconData: IconData(
queryDocSnapshot.data()['ref'],
fontFamily: 'MaterialIcons',
),
),
),
)
.toList(),
),
),
),
);
}
}

Related

Flutter streambuilder returns wrong titles

I recieve ’title’ from firebase in a streambuilder but it gets all titles from all documents. I just want titles from the selected document.
StreamBuilder(
stream: Firestore.instance
.collection(widget.user.uid)
.orderBy('date', descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
return ListView(
shrinkWrap: true,
children: snapshot.data.documents.map((document) {
final current = document.data;
final activities = current["activities"] as List;
List titles =
activities.map((e) => e["title"]).toList();
return Center(
child: Container(
width:
MediaQuery.of(context).size.width / 1.2,
height:
MediaQuery.of(context).size.height / 6,
child: Text("Title: $titles"),
),
);
}).toList(),
);
}
return const Text("no data");
},
)
What am I doing wrong? I just want to display title: lkjn and title:99okkj in a listview.
Just wrap widget creation inside of activitites.map(...) operation and make your widget list flat
Here is the updated version of the build method:
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
shrinkWrap: true,
children: snapshot.data.documents.map((document) {
final current = document.data;
final activities = current["activities"] as List;
return activities.map((e) => Center(
child: Container(
width:
MediaQuery.of(context).size.width / 1.2,
height:
MediaQuery.of(context).size.height / 6,
child: Text("Title: ${e["title"]}"),
),
)
);
}).expand((element) => element).toList(),
));
}

How can I show alert dialog on base of a stream in case network request fails

Here goes the code I have so far.
_mBlock.mSpotStream is a network request.
I am interested how can I show alert dialog in case _mBlock.getSpots() fails with a network error, while keeping list on screen. I have tried returning alert dialog as a widget, but in this case I can't close it.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(Strings.of(context).spot_list_title), centerTitle: true),
body: Container(
child: Column(
children: [
Expanded(
child: Stack(
children: [
StreamBuilder<List<SpotDto>>(
stream: _mBlock.mSpotStream,
builder: (context, snapshot) {
return RefreshIndicator(
onRefresh: () {
return _mBlock.getSpots();
},
child: ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, position) {
return SpotListItem(snapshot.data[position], () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(position.toString())));
});
},
),
);
},
),
Column(
children: [
Expanded(
child: StreamBuilder<Progress<bool>>(
stream: _mBlock.mStateStream,
builder: (context, snapshot) {
return Visibility(
visible: snapshot.data?.mIsLoading ?? false,
child: SizedBox.expand(
child: Container(
color: Colors.blue.withOpacity(Dimens.overlayOpacity),
child: Center(
child: CircularProgressIndicator(),
),
),
),
);
},
),
)
],
)
],
))
],
)),
);
}
}
showAlertDialog(BuildContext context, SpotListBlock block) {
StreamBuilder<Error<String>>(
stream: block.mErrorStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return AlertDialog(
title: Text(Strings.of(context).error),
content: Text(snapshot.data.mErrorMessage),
actions: [
FlatButton(
child: Text("Cancel"),
onPressed: () {
Navigator.pop(context, true);
},
)
],
);
} else {
return Row();
}
},
);
}
In the end, I have fixed it like this, it was the only way I was able to fix this, any reviews are appreciated:
My fix is based on this gist https://gist.github.com/felangel/75f1ca6fc954f3672daf7962577d56f5
class SpotListScreen extends StatelessWidget {
final SpotListBlock _mBlock = SpotListBlock();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(Strings.of(context).spot_list_title), centerTitle: true),
body: Container(
child: Column(
children: [
Expanded(
child: Stack(
children: [
StreamBuilder<List<SpotDto>>(
stream: _mBlock.mSpotStream,
builder: (context, snapshot) {
return RefreshIndicator(
onRefresh: () {
return _mBlock.getSpots();
},
child: ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, position) {
return SpotListItem(snapshot.data[position], () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(position.toString())));
});
},
),
);
},
),
StreamBuilder<Error<String>>(
stream: _mBlock.mErrorStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
SchedulerBinding.instance.addPostFrameCallback((_) {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text('dismiss'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
},
);
});
return Container(
width: 0.0,
height: 0.0,
);
} else {
return Container(
width: 0.0,
height: 0.0,
);
}
},
),
Column(
children: [
Expanded(
child: StreamBuilder<Progress<bool>>(
stream: _mBlock.mStateStream,
builder: (context, snapshot) {
return Visibility(
visible: snapshot.data?.mIsLoading ?? false,
child: SizedBox.expand(
child: Container(
color: Colors.blue.withOpacity(Dimens.overlayOpacity),
child: Center(
child: CircularProgressIndicator(),
),
),
),
);
},
),
)
],
)
],
))
],
)),
);
}
}
bloc code
Future<List<SpotDto>> getSpots() {
var completer = new Completer<List<SpotDto>>();
_reportsRepositoryImpl.getSpots().single.then((spotList) {
addNewSpotsToList(spotList);
completer.complete(spotList);
}).catchError((Object obj) {
switch (obj.runtimeType) {
case DioError:
_mErrorSink.add(Error((obj as DioError).message));
completer.complete();
break;
default:
completer.complete();
}
_mSpotSink.add(_mSpotList);
});
return completer.future;
}
Showing an alert dialog is just a simple call, e.g.:
await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: 'alert!!!',
content: 'hello world',
actions: [
FlatButton(child: Text('cancel'), onPressed: () => Navigator.pop(context, false)),
FlatButton(child: Text('ok'), onPressed: () => Navigator.pop(context, true)),
],
);
},
)
when you call showDialog a dialog will be shown on the screen.

How do you remove an item from from itembuilder using Dissmissable and the item is from Firestore?

#override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('users')
.document(widget.uid)
.collection('lists')
.snapshots(),
builder: (context, snapshot) {
return !snapshot.hasData
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot data = snapshot.data.documents[index];
final list = snapshot.data.documents;
return Dismissible(
// Dismissed function
key: UniqueKey(),
onDismissed: (DismissDirection direction) {
setState(() {
// This is where I would like to remove the element
// snapshot.data.remove(index);
});
},
secondaryBackground: Container(
child: Center(
child: Text('Delete',
style: TextStyle(color: Colors.white),
textAlign: TextAlign.start),
),
color: Colors.red,
),
background: Container(),
child: Card(
child: Center(
child: new Container(
padding: new EdgeInsets.all(32.0),
child: new Column(
children: <Widget>[
new Text(data['listName']),
new Text(data['Description'])
],
),
),
),
),
direction: DismissDirection.endToStart,
);
},
);
},
),
The whole goal is to make a card element disappear and delete that item too from the Firestore.
I thought about placing each item into a map then building the cards from the map. I would rather not do it that way.
Any help is appreciated and hopefully, we can solve this together :)
Thank you in advance!
Get the document reference first by doing this:
Iterate over this: snapshot.data.documents then snapshot.data.documents[i].documentID
then this:
onDismissed: (direction) async {
await Firestore.instance.runTransaction(
(transaction) async {
await transaction.delete(your doc ref here);
},
);
},

How can I access the innermost documents in nested collection structures?

As seen in the picture, there is a collection structure within the firestore. I want to show it with a listview by reaching the document information at the end. But I can't view it on the screen.
Code here:
#override
Widget build(BuildContext context) {
randevular = databaseRef
.collection(
'kuaforumDB/$_salonID/BekleyenRandevular/')
.snapshots();
return StreamBuilder<QuerySnapshot>(
stream: randevular,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if(!snapshot.hasData) {
return Column(
children:<Widget> [
SizedBox(
height: 100,
),
Center(
child: Image.asset("assets/images/icons/fon.webp",matchTextDirection: true,
height: 140.0,
width: 140.0,
),),
SizedBox(
height: 20
),
Center(
child: new Text('Henüz bir randevu oluşturmadınız.')
)
],
);
}
else if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: new Center(
child: new CircularProgressIndicator(
value: null,
strokeWidth: 7.0,
),
)
);
} else {
return ListView(
children: snapshot.data.documents
.map((document) {
var query = databaseRef
.collection('kuaforumDB/')
.document('$_salonID')
.collection('BekleyenRandevular')
.document(document.documentID)
.collection('get')
.snapshots();
return StreamBuilder<QuerySnapshot> (
stream: query,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot2){
if (!snapshot2.hasData) return Text("Loading...");
return ListView(
children: snapshot2.data.documents
.map((DocumentSnapshot doc) => Card(
child: ListTile(
leading: IconButton(
tooltip: '',
icon: const Icon(Icons.check_circle, color: Colors.red,),
color: doc['randevuTarih']
.toDate()
.isBefore(DateTime.now())
? Colors.green
: Colors.orangeAccent,
iconSize: 30,
onPressed: () {},
),
title: Text(AppConstants.formatter
.format((doc['randevuTarih'].toDate())
.add(Duration(hours: 0)))
.toString()),
subtitle: Text('Randevu Onay Bekleniyor.'),
trailing: Icon(Icons.keyboard_arrow_right,
color: Colors.grey, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (content) => MyPendingDetailPage(
salonID: _salonID.toString(),
userID: mPhone,
randevuID:
doc.documentID.toString(),
randevuTarih: AppConstants
.formatter
.format((doc['randevuTarih']
.toDate())
.add(Duration(hours: 0)))
.toString(),
randevuHizmet: doc['hizmetler'],
randevuFiyat:
doc['fiyat'].toString(),
randevuSure:
doc['sure'].toString(),
randevuFavori:
doc['favori'] == null
? false
: doc['favori'],
randevuBittimi:
doc['randevuTarih']
.toDate()
.isBefore(
DateTime.now())
? true
: false,
ayBasi: startofmonth,
sonrandevu : doc['randevuTarih'],
)));
}, )))
.toList(),
);
},
);
}).toList());
}
});
}
Using nested Listview in the code above may have caused a question. But I don't know how to solve this. When I check it, I see that I can actually pull the data, but I can't show it on the screen.

UnimplementedError error in FutureBuilder while displaying inserted data from database

I'm trying to create a Futurebuilder function to call and display all data that inserted in database unfortunately I got this error 'UnimplementedError' and im pretty stock on this any suggestion will be appreciated.
Here in my full code for implementation to display data in been trying to fix my error 'UnimplementedError' in which I'm trying to do is to display inserted in list view not in web view any suggestion will be appreciated.
body: Center(
child: Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<ContactsDao>(
future: _calltheStream(),
builder: (BuildContext context,
AsyncSnapshot<ContactsDao> snapshot) {
if (!snapshot.hasData ||
snapshot.connectionState == ConnectionState.none) {
return Container(
child: CircularProgressIndicator(),
);
} else {
return StreamBuilder<List<ContactObject>>(
stream: snapshot.data.findallContactsById(),
builder: (context, snapshot) {
if (!snapshot.hasData ||
snapshot.connectionState ==
ConnectionState.none) {
return Container(
child: CircularProgressIndicator(),
);
} else {
if(widget.Contactlist.length != snapshot.data.length){
widget.Contactlist = snapshot.data;
}
if(snapshot.data.length == 0){
return Center(
child: Text('No Data Found'),
);
}
return Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int index) {
return Card(
child: ListTile(
leading: Checkbox(
value: widget.Contactlist[index].isSelect,
onChanged: (bool value) {
setState(() {
widget.Contactlist[index].isSelect = value;
});
},
),
trailing: GestureDetector(
onTap: () {
_selectedDetele(snapshot.data[index].id);
},
child: Icon(Icons.delete),
),
title: Text('${snapshot.data[index].task}',maxLines: 1,),
subtitle: Text('${snapshot.data[index].time}',style: TextStyle(fontSize: 10),),
));
}),
);
}
}); //DATA
} //DATA
}), // DATA
], // DATA
), // DATA
),//DATA
),
Future<ContactsDao> _calltheStream() async { //GET ALL DATA HERE
ContactDatabase contactDatabase = await widget.database;
widget._contactsdao = contactDatabase.contactsDao;
return contactDatabase.contactsDao;
}