Problem pageview reload first page after setState ( Flutter ) - 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;
}
});
}

Related

How to pass context to a function in a class which I am mapping over in Flutter?

I have several widgets in my app which build "cards" (ListTiles) by mapping data as follows:
return FutureBuilder<List<MyCard>>(
future: MyCard.readData(snapshot.data),
builder: (context, cards) {
if (cards.hasData) {
final card = cards.data!;
return Expanded(
child: ListView(
padding: const EdgeInsets.all(16),
children: card.map(MyCard.buildCard).toList()));
} else {
return const Text("No data");
}
});
The method buildCard (for MyCard class) is as follows:
static Widget buildCard(MyCard card) {
var dateFormat = DateFormat('MM/dd/yyyy');
return Column(
children: [
Align(
alignment: Alignment.centerRight,
child: Text(dateFormat.format(card.createdOn.toDate()))),
const SizedBox(height: 6),
ListTile(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
tileColor: Colors.white,
leading: CircleAvatar(child: Text(card.subCategory)),
title: Text("Score: " + card.score + " Misses: " + card.misses),
subtitle: card.comment.isNotEmpty
? Text("Comment(s): " + card.comment)
: null,
trailing: IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: checkCard(card)), //need to pass context to checkCard
),
const SizedBox(height: 18),
],
);
}
But I need to get the context to the buildCard method (because I need to pass it to the checkCard method since I am trying to call showDialog() in said method and I can't figure out how to pass the context.
I tried to simply add the BuildContext field to the buildCard method as follows:
static Widget buildCard(MyCard card, BuildContext context) {
var dateFormat = DateFormat('MM/dd/yyyy');
return Column(
children: [
Align(
alignment: Alignment.centerRight,
child: Text(dateFormat.format(card.createdOn.toDate()))),
const SizedBox(height: 6),
ListTile(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
tileColor: Colors.white,
leading: CircleAvatar(child: Text(card.subCategory)),
title: Text("Score: " + card.score + " Misses: " + card.misses),
subtitle: card.comment.isNotEmpty
? Text("Comment(s): " + card.comment)
: null,
trailing: IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: checkCard(card, context)),
),
const SizedBox(height: 18),
],
);
}
but when I try to pass it to the method in my Widgets it doesn't seem to work. I figured I would pass it as follows:
return FutureBuilder<List<MyCard>>(
future: MyCard.readData(snapshot.data),
builder: (context, cards) {
if (cards.hasData) {
final card = cards.data!;
return Expanded(
child: ListView(
padding: const EdgeInsets.all(16),
children: card.map(MyCard.buildCard(context)).toList()));
}
But get the following:
Try the following code:
ListView.builder(
itemCount: card.length,
itemBuilder: (context, index) {
return MyCard.buildCard(card[index], context);
},
),
Change your code from
ListView(
padding: const EdgeInsets.all(16),
children: card.map(MyCard.buildCard(context)).toList()));
to
ListView.builder(
itemCount: card.length,
itemBuilder: (context, index) {
return MyCard.buildCard(card[index], context); 👈 Pass your context here which is received from the itemBuilder
},
),

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

Flutter page jumps to top after setState({})

I display many images in a Staggered Gridview in a Flutter application.
Everytime I call setState({}), for example after deleting an item, the page jumps to top. How could I remove this behavior?
This is my code:
final _scaffoldKey = new GlobalKey<ScaffoldState>();
.. outside the build function. And then...
return loadingScreen == true
? LoadingScreen()
: Scaffold(
key: _scaffoldKey,
body: CustomScrollView(
slivers: <Widget>[
_AppBar(
theme: theme,
index: index,
albumImagePath: albumImagePath,
albumID: albumID,
albumValue: albumValue,
addPictureToGallery: _addPictureToGallery,
),
SliverToBoxAdapter(
child: Column(
children: <Widget>[
InfoBar(
albumPicturesSum: albumPicturesSum,
getBilderString: _getBilderString,
theme: theme,
getVideoProgress: _getVideoProgress,
progress: progress,
),
albumID == 99999999
? // Demo Projekt
DemoImageGrid(
demoImageList: demoImageList,
getDemoImagesJson: _getDemoImagesJson,
)
: UserImageGrid(
picturesData: picturesData,
albumID: albumID,
showPictureActions: _showPictureActions)
],
),
)
],
),
);
}
The UserImageGrid looks like the following:
class UserImageGrid extends StatelessWidget {
final Pictures picturesData;
final int albumID;
final Function showPictureActions;
final _key = new UniqueKey();
UserImageGrid(
{#required this.picturesData,
#required this.albumID,
#required this.showPictureActions});
#override
Widget build(BuildContext context) {
return FutureBuilder(
key: _key,
future: picturesData.getPicturesFromAlbum(albumID),
builder: (BuildContext context, AsyncSnapshot snapshot) {
// Normale Projekte
if (snapshot.hasData && snapshot.data.length == 0) {
return Center(
child: Column(
children: <Widget>[
Lottie.asset('assets/lottie/drone.json',
width: 250,
options: LottieOptions(enableMergePaths: false)),
],
),
);
}
if (!snapshot.hasData ||
snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Container(
child: StaggeredGridView.countBuilder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.all(0),
crossAxisCount: 6,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) =>
GestureDetector(
onLongPress: () {
showPictureActions(snapshot.data[index]);
},
onTap: () async {
await showDialog(
context: context,
builder: (_) {
return Dialog(
child: Stack(
children: [
Container(
margin: const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 10.0,
),
height: 500.0,
child: ClipRect(
child: PhotoView(
maxScale:
PhotoViewComputedScale.covered * 2.0,
minScale:
PhotoViewComputedScale.contained *
0.8,
initialScale:
PhotoViewComputedScale.covered,
imageProvider: FileImage(
File(snapshot.data[index].path))),
),
),
Positioned(
bottom: 20,
left: 20,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
DateFormat(tr("date_format")).format(
snapshot.data[index].timestamp
.toDateTime()),
style: TextStyle(color: Colors.white),
),
),
)
],
));
});
},
child: Container(
child: Image.file(
File(snapshot.data[index].thumbPath),
fit: BoxFit.cover,
)),
),
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(2, index.isEven ? 2 : 2),
mainAxisSpacing: 5.0,
crossAxisSpacing: 5.0,
),
);
}
});
}
}
What could be the issue?
I found a solution for this issue. The problem was not the setState({}). It was the return Widget of the FutureBuilder.
I changed
if (!snapshot.hasData || snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
to:
if (!snapshot.hasData || snapshot.connectionState == ConnectionState.waiting) {
return Container(
height: MediaQuery.of(context).size.height,
);
}
I don´t exactly know why, but with this change the page is not jumping to top anymore on setState({})

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.

Using a CachedVideoPlayer in a listview

I am attempting to show videos in a listview that is preventing me from declaring the videocontroller in the initState. This causes me to accidentally be redrawing the video multiple times during the application. I am receiving this error:
FATAL EXCEPTION: ExoPlayerImplInternal:Handler
then
java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available
with my current implementation. It appears to work fora while but the memory slowly builds up until it is full. How can I implement this differently?
here is the code I am calling in the stream:
Widget getVideoItem(DocumentSnapshot doc) {
if (watchList.contains(doc['user'])) watched = true;
DateTime dateTime = DateTime.parse(doc['time']);
_videoPlayerController = CachedVideoPlayerController.network(doc["downUrl"])
..initialize();
_videoPlayerController.setLooping(true);
_videoPlayerController.play();
volumeOn = sharedPreferences.getBool("vidVol");
if (volumeOn == null) {
sharedPreferences.setBool("vidVol", false);
volumeOn = false;
}
if (volumeOn) {
_videoPlayerController.setVolume(1.0);
} else {
_videoPlayerController.setVolume(0.0);
}
return new FutureBuilder(
future: getUserData(doc["user"]),
builder: (BuildContext context, snapshot) {
return SizedBox(
height: MediaQuery.of(context).size.width + 140,
width: MediaQuery.of(context).size.width,
child: Column(children: <Widget>[
new ListTile(
title: new Text(userInfo),
subtitle: new Text(doc["title"]),
leading: FutureBuilder(
future: getProfUrl(doc),
builder: (BuildContext context, snapshot) {
Widget child;
if (!snapshot.hasData) {
child = _showCircularProgress();
} else {
child = child = new Container(
width: 44.0,
height: 44.0,
child: CachedNetworkImage(
imageUrl: doc["profUrl"],
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
),
);
}
return child;
}),
),
new Padding(
padding: EdgeInsets.fromLTRB(4, 4, 4, 4),
child: FutureBuilder(
future: getDownUrl(doc),
builder: (BuildContext context, snapshot) {
List<Widget> children;
if (!snapshot.hasData) {
children = [_showCircularProgress()];
} else {
children = [
Center(
child: new AspectRatio(
aspectRatio: 1 / 1,
child: Stack(
children: [
VisibilityDetector(
key: Key("unique key"),
onVisibilityChanged: (VisibilityInfo info) {
if (info.visibleFraction > .20) {
_videoPlayerController.pause();
} else {
_videoPlayerController.play();
}
},
child: CachedVideoPlayer(
_videoPlayerController,
)),
IconButton(
icon: volumeOn
? Icon(Icons.volume_up)
: Icon(Icons.volume_off),
onPressed: () {
setState(() {
_videoPlayerController.pause();
sharedPreferences.setBool(
"vidVol", !volumeOn);
});
},
),
],
),
),
)
];
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
),
);
}),
),
new Row(
children: [
new IconButton(
icon: !watched
? new Icon(
Icons.remove_red_eye,
color: Colors.black26,
)
: new Icon(
Icons.remove_red_eye,
color: Colors.blueGrey[400],
),
onPressed: () {
initToggleWatched(watchList, doc["user"], name, position,
secPosition, state, year, user);
}),
Padding(
padding: EdgeInsets.fromLTRB(5, 0, 0, 0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
dateTime.day.toString() +
"/" +
dateTime.month.toString() +
"/" +
dateTime.year.toString(),
style: TextStyle(color: Colors.black26, fontSize: 12),
),
),
),
],
)
]),
);
},
);
}
Try making the widget with a controller a separate StatefullWidget instead of putting everything in one place and manage the instantiation and disposal of the controller in the initState() and dispose() methods.