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

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.

Related

The element type 'Future<Widget>' can't be assigned to the list type 'Widget'

I tried couple of solutions given but nothing worked for me
//this basically lays out the structure of the screen
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.only(
top: 30,
bottom: 60,
),
child: Column(
children: [
buildTitle(),
SizedBox(
height: 50,
),
buildForm(),
Spacer(),
buildBottom(),
],
),
),
);
}
//problem is with buildForm
Future<Widget> buildForm() async {
final valid = await usernameCheck(this.username);
return Container(
width: 330,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Form(
key: _userNameformKey,
child: TextFormField(
textAlign: TextAlign.center,
onChanged: (value) {
_userNameformKey.currentState.validate();
},
validator: (value) {
if (value.isEmpty ) {
setState(() {
onNextButtonClick = null;
});
}
else if(!valid){
setState(() {
//user.user.username=value;
onNextButtonClick = null;
showDialog(
context: context,
builder: (context) =>
new AlertDialog(
title: new Text('Status'),
content: Text(
'Username already taken'),
actions: <Widget>[
new ElevatedButton(
onPressed: () {
Navigator.of(context, rootNavigator: true)
.pop(); // dismisses only the dialog and returns nothing
},
child: new Text('OK'),
),
],
),
);
Try using FutureBuilder<T>
Widget build(_) {
return FutureBuilder<bool>(
future: usernameCheck(this.username),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if(!snapshot.hasData) { // not loaded
return const CircularProgressIndicator();
} else if(snapshot.hasError) { // some error
return const ErrorWidget(); // create this class
} else { // loaded
bool valid = snapshot.data;
return Container(/*...details omitted for conciseness...*/);
}
}
)
}

Data From multiple FutureBuilders in flutter

I'm fetching data from an api source , the data is fetched properly , then i store the data in sqflite , so basically after doing both , i need to check if there is connection so that i show data from internet other than that i get data back from database , now since i'm using futurebuilder which return internet async operation result , how would i be also to get list of data from database , any help is appreciated guys and thank you in advance.
This is what i have tried so far
#override
void initState() {
super.initState();
dbHelper = DbHelper();
}
#override
Widget build(BuildContext context) {
return Scaffold (
appBar: AppBar(
title: Text("News Application"),
centerTitle: true,
backgroundColor: Colors.black,
titleTextStyle: TextStyle(color: Colors.white),
),
body: FutureBuilder (
future: Future.wait([getEverything(),dbHelper.getAllNews()]),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
if(snapshot.hasError) {
// So basically here if there is an error , i woul like to show data from database
// i tried to get data from snapshot like this : snapshot.data[0]...and snapshot.data[1]
// but no data is returned..
return new Center(
child: new CircularProgressIndicator(
backgroundColor: Colors.black,
),
);
} else {
if(snapshot.connectionState == ConnectionState.done){
return new Container(
color: Colors.black,
child: GridView.count(
padding: const EdgeInsets.all(20),
crossAxisCount: 2,
children: List.generate(snapshot.data.articles.length, (index) {
return new GestureDetector(
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailsScreen(
image: snapshot.data.articles[index].urlToImage,
author: snapshot.data.articles[index].author,
title: snapshot.data.articles[index].title,
description: snapshot.data.articles[index].description,
publishedAt: snapshot.data.articles[index].publishedAt,
content: snapshot.data.articles[index].content,
))
);
},
child: Card(
elevation: 12,
child: new Column(
children: [
Image.network(snapshot.data.articles[index].urlToImage,
width: 250,),
Text(snapshot.data.articles[index].description)
],
),
),
);
}
)));
}
}
return new Center(
child: Visibility(
visible: true,
child: CircularProgressIndicator(
backgroundColor: Colors.black,
),
),
);
},
),
);
}

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 I fix a flickering (blinking) menu screen?

I have added a GIF where you can see that when I click on favourite (heart) menu it loads smoothly and doesn't flick but when I click on side menu it flicks
I am working on an app and I am trying to resolve an issue, there is a screen whenever I click it, it flicks or blinks, it kind of refreshes. All the content it contain refresh, like if I click it I feel like I come on that screen then the items come there later on, it happens in few frictions of seconds but it is too annoying. I tried everything but still.
I tried to slow down the animation globally by using time dilation but it is still happening.
const String logo = "assets/profile/images/stadia_logo.svg";
class ProfileScreen extends StatefulWidget {
#override
_ProfileScreenState createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
final StorageService ref = StorageService();
final screenHeight = MediaQuery.of(context).size.height;
final screenWidth = MediaQuery.of(context).size.width;
final logoHeight = screenHeight * 0.4;
return Scaffold(
backgroundColor: Colors.white,
body: FutureBuilder(
future: ref.getProfileUrl(user.uid),
builder: (BuildContext context, AsyncSnapshot<String> url) {
if (url.connectionState == ConnectionState.waiting) {
return Loading(
size: 20,
);
}
if (url.hasData) {
return StreamBuilder<DocumentSnapshot>(
stream: DatabaseService(uid: user.uid).userData,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Loading(
size: 15,
);
}
if (snapshot.data == null) {
return Loading(
size: 15,
);
}
var userData = snapshot.data;
String _imageUrl = url.data;
if (snapshot.hasData) {
List books = userData['audio_read'];
if (books == null) {
return Loading(
size: 15,
);
//this
}
return Scaffold(
body: Stack(
children: <Widget>[
Transform.translate(
offset: Offset(screenWidth * 0.4, 10),
child: Transform.rotate(
angle: -0.1,
child: SvgPicture.asset(
logo,
height: logoHeight,
color: logoTintColor,
),
),
),
SingleChildScrollView(
child: Column(
children: <Widget>[
SizedBox(
height: 60,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
//
createFirstNameHeader(
user, ref, userData),
Padding(
padding: const EdgeInsets.only(
right: 16.0, bottom: 16, top: 16),
child: Material(
elevation: 4,
borderRadius:
const BorderRadius.all(
Radius.circular(12)),
child: Padding(
padding: const EdgeInsets.only(
left: 16.0,
right: 16.0,
top: 16.0,
bottom: 32.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Text(
"TOTAL TALES PLAYED",
style:
hoursPlayedLabelTextStyle,
),
],
),
SizedBox(
height: 4,
),
Text(
"${books.length.toString()} ${books.length > 1 ? "Tales" : "Tale"}",
style: hoursPlayedTextStyle
.copyWith(
color: Color(
0xff5E17EB)),
)
],
),
),
),
),
ContentHeadingWidget(
heading: "Continue listening"),
for (var i = books.length - 1;
i >= 0;
i--)
books.length > 0
? Slidable(
actionExtentRatio: 0.25,
actionPane:
SlidableDrawerActionPane(),
secondaryActions: <Widget>[
IconSlideAction(
caption: 'Delete',
color: Colors.red,
icon: Icons.delete,
onTap: () => {
_deletePressed(
user,
books.elementAt(
i)[
'audio_id']),
}),
],
child: LastReadBook(
audioId: books.elementAt(
i)['audio_id'],
audio_index: int.parse(
books.elementAt(i)[
'audio_index']),
audioSeek: books.elementAt(
i)['audio_seek'],
total: books.elementAt(
i)['total'],
progress: int.parse(books.elementAt(i)['audio_index']) /
books.elementAt(
i)["totalIndex"],
description:
"${books.elementAt(i)["audio_index"]} of ${books.elementAt(i)["totalIndex"]} episodes",
image: books
.elementAt(i)['image']),
)
: Loading(
size: 15,
),
],
),
),
],
),
)
],
),
);
} else {
return Loading(
size: 15,
);
}
});
} else {
return Loading(
size: 15,
);
}
}),
);
}
Padding createFirstNameHeader(
User user, StorageService ref, DocumentSnapshot userData) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0,
),
child: Row(
children: <Widget>[
createProfileImage(user, ref),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: 'Hello',
style: userNameTextStyle,
),
TextSpan(text: '\n'),
TextSpan(
text: userData['firstname'], style: userNameTextStyle),
],
),
),
),
],
),
);
}
createProfileImage(User user, StorageService ref) {
return StreamBuilder(
stream: Firestore.instance
.collection("Users")
.document(user.uid)
.snapshots(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
// return Center(
// child: CircularProgressIndicator(),
// );
return Loading(
size: 15,
);
}
if (snapshot.data == null) {
return Loading(
size: 15,
);
}
print(snapshot.data.data);
return GestureDetector(
onTap: () async {
if (user.uid != HomeScreen.defaultUID) {
dynamic imageFromUser = await ref.getImage();
await ref.addImageToFirebase(user.uid, imageFromUser);
} else {
createAccountDialouge(context);
}
},
child: RoundedImageWidget(
imagePath: snapshot.data["profile_image"],
icon: Icons.photo_camera,
showIcon: true),
);
});
}
createAccountDialouge(BuildContext context) {
return showDialog(
context: context,
child: CupertinoAlertDialog(
title: Text('Avid Tale Would Like to Enable Full Access'),
content: Text('Creating an account will enable access to this feature'),
actions: <Widget>[
CupertinoDialogAction(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
CupertinoDialogAction(
child: Text('Register'),
onPressed: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return RegisterScreen();
},
),
);
},
)
],
),
);
}
void _deletePressed(user, bookId) async {
DatabaseService userService = DatabaseService(uid: user.uid);
DocumentSnapshot documentSnapshot =
await Firestore.instance.collection("Users").document(user.uid).get();
var data = documentSnapshot.data["audio_read"];
data.retainWhere((element) => element["audio_id"] != bookId);
await userService.updateUserData({'audio_read': FieldValue.delete()});
await userService
.updateUserData({'audio_read': FieldValue.arrayUnion(data)});
}
}
Every time you delete an item, it triggers a new event in the Stream. As the Stream is retrieving/processing the new data it goes into this if block of code as the connectionState changes to waiting:
if (snapshot.connectionState == ConnectionState.waiting) {
return Loading(
size: 15,
);
}
Simply removing this block of code will should stop the "flickering". You may need to include other checks of data availability if necessary for your application.

Problem pageview reload first page after setState ( Flutter )

I have a code, this code create a pageview about some user, data is get from firebase
return new Scaffold(
appBar: new AppBar(
title: new Text("Carousel"),
),
body: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('users').snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new CircularProgressIndicator();
default:
return new PageView(
onPageChanged: _onPageViewChange,
controller: _controller,
scrollDirection: Axis.horizontal,
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return new Column(
children: <Widget>[
new Container(
child: new ClipOval(
child: new CachedNetworkImage(
width: 150.0,
height: 150.0,
imageUrl: document['img'],
fit: BoxFit.fill,
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) =>
Icon(Icons.error),
)),
),
new ListTile(
title: new Text(
isPerson
? 'My name is'
: (isPlace
? 'My favourite is'
: (isNote
? 'I am from'
: (isPhone
? 'My phone is'
: (isLock ? '' : '')))),
textAlign: TextAlign.center),
subtitle: new Text(
isPerson
? document['name']
: (isPlace
? document['place']
: (isNote
? document['note']
: (isPhone
? document['phone']
: (isLock
? document['lock'].toString()
: "")))),
textAlign: TextAlign.center,
),
),
buildButton1(Icons.person)
],
);
}).toList(),
);
}
},
));
}
this is fuction buildButton1()
Widget buildButton1(IconData icon) {
return new Column(
children: <Widget>[
new Container(
padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 20.0),
child: new IconButton(
icon: Icon(icon),
onPressed: () {
setState(() {
//isChecked ? true : false;
isPerson = true;
isNote = false;
isPlace = false;
isPhone = false;
isLock = false;
});
},
iconSize: 32.0,
color: isPerson ? Colors.green : Colors.grey,
),
)
],
);
}
When I press a button to set variable then Pageview reload and show firstpage. How can I solved this problem. This is example picture https://imgur.com/nKC358E
................................................................................................
The issue comes from the _onPageViewChange function.
The last page doesn't return an integer value. If you have 3 pages, than the last returned index will be 1.99999999999... and not 2.
I solved the problem like this
onPageChanged: (index){
setState(() {
if (index > 1.99){
lastPage=true;
}else{
lastPage=false;
}
});
}