How can I get data from three separate FutureBuilders widgets? - flutter

I am trying to display one SingleChildListView containing THREE seperate FutureBuilder with ListView.sperator. I am using Provider for fetching data from the SQFLite database.
Thi is my code:
class SelectCategoryPage extends StatefulWidget {
const SelectCategoryPage({Key? key}) : super(key: key);
#override
State<SelectCategoryPage> createState() => _SelectCategoryPageState();
}
class _SelectCategoryPageState extends State<SelectCategoryPage> {
//
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kBackgroundColor,
appBar: _appBar(),
body: _body(context),
bottomNavigationBar: _bottomNavigationBar(),
);
}
AppBar _appBar() {
return AppBar(
elevation: 0,
backgroundColor: Colors.white,
title: Text(
'Select Category',
style: appBarHeaderTStyle,
),
iconTheme: const IconThemeData(color: Colors.black),
actions: [
Consumer<SelectCategoryViewModel>(
builder: (context, provider, child) {
return TextButton(
child: Text(
provider.getEditOptionData ? 'Save' : 'Edit',
style: textButtonTStyle,
),
onPressed: () {
provider.toggleEditButton();
},
);
},
),
],
);
}
Widget _body(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 20, left: 24, right: 24),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
controller: null,
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 12),
Text(
'Expense',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_expenseCategory(context),
//
const SizedBox(height: 24),
Text(
'Income',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_incomeCategory(context),
//
const SizedBox(height: 24),
Text(
'Other',
style: selectCategoryHeaderTStyle,
),
const SizedBox(height: 16),
//
_otherCategory(context),
//
const SizedBox(height: 20),
],
),
),
),
],
),
);
}
FutureBuilder<void> _expenseCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByExpenseType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, expenseProvider, child) => expenseProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: expenseProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: expenseProvider.data[index].id,
coloredIcon: expenseProvider.data[index].categoryIcon,
title: expenseProvider.data[index].categoryName,
isOptionDotsVisitable:
expenseProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
FutureBuilder<void> _incomeCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByIncomeType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, incomeProvider, child) => incomeProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: incomeProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: incomeProvider.data[index].id,
coloredIcon: incomeProvider.data[index].categoryIcon,
title: incomeProvider.data[index].categoryName,
isOptionDotsVisitable:
incomeProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
FutureBuilder<void> _otherCategory(BuildContext context) {
return FutureBuilder(
future: Provider.of<SelectCategoryViewModel>(
context,
listen: false,
).selectCategoryByOtherType(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
return Consumer<SelectCategoryViewModel>(
child: const Center(child: Text('No Data')),
builder: (context, otherProvider, child) => otherProvider
.data.isEmpty
? child!
: ListView.separated(
shrinkWrap: true,
itemCount: otherProvider.data.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 16);
},
itemBuilder: (BuildContext context, int index) {
return SelectCategoryCard(
id: otherProvider.data[index].id,
coloredIcon: otherProvider.data[index].categoryIcon,
title: otherProvider.data[index].categoryName,
isOptionDotsVisitable:
otherProvider.getEditOptionData,
);
},
),
);
default:
return Container();
}
},
);
}
Widget _bottomNavigationBar() {
return CustomBottomAppBar(
buttonText: '+ Add New Category',
onTapEvent: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CreateNewCategoryPage(),
),
);
},
);
}
}
Only the last FutureBuilder is working properly, and other builders are showing the exact output as the last (3rd) FutureBuilder.

Went through the same thing once. You need to have only one future builder and a Future that gets all the data and assigns it. Something like this:
class _DeviceInformationScreenState extends State<DeviceInformationScreen> {
late StaticConfiguration staticConfiguration;
late PackageInfo packageInfo;
late DeviceData deviceData;
late QuarkusHealthClient quarkusHealth;
LisaInfo lisaInfo = LisaInfo();
//TODO: add translations?
Future<void> _getAppInfo() async {
packageInfo = await PackageInfo.fromPlatform();
staticConfiguration = await config.readStaticConfiguration();
deviceData = await DeviceData.fromDevice();
quarkusHealth = await QuarkusHealthClient.checkHealth(staticConfiguration.grpcServerHost);
lisaInfo = await lisaInfo.getLisaInfo();
}
#override
Widget build(BuildContext context) {
return buildToolsScaffold(context,
body: FutureBuilder(
future: _getAppInfo(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 50.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Text(
T.screens.healthInfo.configData,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 20,
),
_buildConfigDataWidgets()
],
),
And _buildConfigDataWidgets() looks like this:
Widget _buildConfigDataWidgets() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(T.screens.healthInfo.data.configFile +
(staticConfiguration.configFileExists
? T.screens.healthInfo.data.exists
: T.screens.healthInfo.data.doesNotExist)),
const SizedBox(
height: 10,
),
Text(T.screens.healthInfo.data.grpcHost + ': ' + staticConfiguration.grpcServerHost),
const SizedBox(
height: 10,
),
Text(T.screens.healthInfo.data.grpcPort + ': ' + staticConfiguration.grpcServerPort.toString()),
const SizedBox(
height: 10,
),
if (staticConfiguration.locale != null) ...[
Text(T.screens.healthInfo.data.activeLanguageCode + ': ' + staticConfiguration.locale!.languageCode),
const SizedBox(
height: 10,
),
if (staticConfiguration.locale!.countryCode != null)
Text(T.screens.healthInfo.data.activeCountryCode + ': ' + staticConfiguration.locale!.countryCode!),
],
],
);
}

Related

Flutter : Auto refresh to update the data

I am developing a cart page which contains + and - buttons, on pressing it , the value in the backend changes, but it doesn't automatically change in the frontend.
Cartpage.dart
class CartUI extends StatefulWidget {
const CartUI({Key? key}) : super(key: key);
#override
State<CartUI> createState() => _CartUIState();
}
class _CartUIState extends State<CartUI> {
#override
Widget build(BuildContext context) {
final user = Provider.of<Userr?>(context, listen: false);
return Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(children: [
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Lottie.asset('assets/animations/delivery.json'),
);
} else {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (BuildContext context, int index) {
final DocumentSnapshot documentSnapshot =
snapshot.data!.docs[index];
return Container(
height: 120,
width: 300,
child: Row(
children: [
Column(
children: [
SizedBox(
width: 200,
child: Text(
documentSnapshot['name'],
style: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
Text(
documentSnapshot['quantity'].toString(),
style: const TextStyle(
fontSize: 15,
),
),
Text(
'Rs.${documentSnapshot['price'].toString()}',
style: const TextStyle(
color: Colors.black87,
fontSize: 15,
),
),
Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
const SizedBox(
width: 40,
),
ElevatedButton(
onPressed: () {
if (documentSnapshot['value'] != 0.0) {
setState(() {
String id = documentSnapshot['docid'];
final user = Provider.of<Userr?>(context, listen: false);
var postDocRef = FirebaseFirestore.instance.collection('myOrders').doc(user?.uid).collection('items').doc();
Provider.of<Calculations>(context, listen: false).updatecartdata(
context,
{
'value': documentSnapshot['value'] - 0.5,
'price': documentSnapshot['price'] - (documentSnapshot['ogprice'] / 2),
},id
);
}
);
}
if (documentSnapshot['value'] ==
0.5) {
String id =
documentSnapshot['docid'];
Provider.of<ManageData>(
context,
listen: false)
.deleteData(context, id);
}
},
child: const Text('-'),
),
const SizedBox(width: 20),
Text(documentSnapshot['value']
.toString()),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
String id =
documentSnapshot['docid'];
final user =
Provider.of<Userr?>(context,
listen: false);
var postDocRef =
FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.doc();
Provider.of<Calculations>(context, listen: false).updatecartdata(
context,
{
'value': documentSnapshot['value'] + 0.5,
'price': documentSnapshot['price'] + (documentSnapshot['ogprice'] / 2),
},id
);
},
child: const Text('+'),
),
]),
],
),
],
),
);
});
}
},
),
),
_BillDetailView(),
]),
],
),
),
);
}
}
class _BillDetailView extends StatelessWidget {
#override
Widget build(BuildContext context) {
final textStyle =
Theme
.of(context)
.textTheme
.bodyText1!
.copyWith(fontSize: 16.0);
return Container(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Bill Details',
style:
Theme
.of(context)
.textTheme
.headline6!
.copyWith(fontSize: 17.0),
),
SizedBox(
height: 5,
),
FutureBuilder(
future: Provider.of<Calculations>(context, listen: false)
.getTotalCost(context),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Item total', style: textStyle),
Text('${snapshot.data}', style: textStyle),
],
);
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error.toString()}");
} else {
return const CircularProgressIndicator();
}
},
),
],
),
);
}
}
Calculation.dart
Future<dynamic> getTotalCost(BuildContext context) async {
final user = Provider.of<Userr?>(context, listen: false);
double totalCost = 0.0;
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.get();
for (var doc in snapshot.docs) {
totalCost += doc["price"];
}
print(totalCost.toString());
return totalCost.toString();
}
The value in the front end changes but last updated(Added or subtracted) value is not reflecting.After hot reload it changes, but not automatically.
How to change this code to automatically update the item total in the front end.
You need to use StreamBuilder instead of FutureBuilder to get live updates.
Changes your Calculation.dart to:
Stream<dynamic> getTotalCost(BuildContext context) async {
final user = Provider.of<Userr?>(context, listen: false);
double totalCost = 0.0;
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.snapshots();
for (var doc in snapshot.docs) {
totalCost += doc["price"];
}
print(totalCost.toString());
return totalCost.toString();
}
And change FutureBuilder to StreamBuilder:
StreamBuilder(
stream: Provider.of<Calculations>(context, listen: false)
.getTotalCost(context),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Item total', style: textStyle),
Text('${snapshot.data}', style: textStyle),
],
);
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error.toString()}");
} else {
return const CircularProgressIndicator();
}
},
),

How to fix Image overflow in Flutter PageView?

I'm building an app where it shows the title, author name, number of upvotes, and an image from the subreddit in a page view. Everything is working fine but for some images, the page view is overflowing, how do I fix this?
Here's the overflow error:
Here's my code
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class Stardew extends StatefulWidget {
const Stardew({ Key? key }) : super(key: key);
#override
State<Stardew> createState() => _StardewState();
}
class _StardewState extends State<Stardew> {
List data = [];
Future<String> getData() async {
List temp_data = [];
var response = await http.get(
Uri.parse("https://m...content-available-to-author-only...p.com/gimme/stardewvalley/100")
);
return response.body;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.data == null){
return Center(child: CircularProgressIndicator(color: Color(0xff008b00)));
}
var jsonData = jsonDecode(snapshot.data);
jsonData = jsonData["memes"];
return PageView.builder(
//scrollDirection: Axis.vertical,
itemCount: jsonData.length,
itemBuilder: (BuildContext context, int index){
return Center(
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
getImgCard(
jsonData[index]["title"],
//jsonData[index]["preview"][2],//preview image
jsonData[index]["url"], //original image
jsonData[index]["author"],
(jsonData[index]["ups"]).toString()
)
],
),
),
);
},
);
}
);
}
Widget getImage(String imgUrl){
return Container(
child: Image.network(
imgUrl,
fit: BoxFit.scaleDown,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded/loadingProgress.expectedTotalBytes! : null,
color: Color(0xff008b00),
),
);
},
),
);
}
Widget getImgCard(String title, String imgUrl, String author, String ups){
return Card(
color: Color(0xff000000),
clipBehavior: Clip.antiAlias,
child: Column(
children: [
ListTile(
leading: RichText(
text: TextSpan(
children: [
TextSpan(
text: ups,
),
const WidgetSpan(
child: Icon(Icons.arrow_upward, size: 18, color: Color(0xff008b00),),
)
],
),
),
title: Text(title, style: TextStyle(color: Colors.white),),
subtitle: Text(
"Posted by u/${author}",
style: TextStyle(color: Colors.white.withOpacity(0.6)),
),
),
getImage(imgUrl),
Padding(padding: EdgeInsets.only(bottom: 8))
],
),
);
}
}
How do I fix this? I have tried changing the box fit and it did not work. Then I used expaned and flexible widgets and still can't find the answer to this solution. please help me.
Wrap getImage(imgUrl) inside Expanded widget.
I found the answer myself, removing the parent column and wrapping it with SingleChildScrollView fixed the error.
return PageView.builder(
//scrollDirection: Axis.vertical,
itemCount: jsonData.length,
itemBuilder: (BuildContext context, int index){
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(1.0),
child: getImgCard(
jsonData[index]["title"],
//jsonData[index]["preview"][2],//preview image
jsonData[index]["url"], //original image
jsonData[index]["author"],
(jsonData[index]["ups"]).toString()
),
)
);
},
);

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.

Flutter: using button to input a static text

I want to make list of button with a static text e.g(lorem, ipsum, etc) for a search bar, so when i hit the button it will input the text into textfield. can it be done in flutter?
Use SeachDelegate delegate
class CustomSearchHintDelegate extends SearchDelegate {
CustomSearchHintDelegate({
String hintText,
}) : super(
searchFieldLabel: hintText,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,
);
#override
Widget buildLeading(BuildContext context) => Text("leading");
#override
Widget buildSuggestions(BuildContext context) => Text("suggestions");
#override
Widget buildSuggestions(BuildContext context) { // This is your list which comes from futurebuilder or streambuilder
// BookModel bookModel = Provider.of(context);
if (query.isNotEmpty)
// return list of books which student wants to search
return Consumer<BookModel>(
builder: (_, bookModel, __) {
return FutureBuilder(
future: bookModel.searchOperation(query),
builder: (BuildContext context, AsyncSnapshot snapshot) {
// switch (snapshot.connectionState) /{
// case ConnectionState.waiting:
// return Center(
// child: CircularProgressIndicator(),
// );
// break;
// case ConnectionState.none:
// return Text("Something went wrong");
// break;
// case ConnectionState.done:
// case ConnectionState.active:
if (!snapshot.hasData)
return Center(
child: CircularProgressIndicator(),
);
return snapshot.hasData
? snapshot.data.length != 0
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
final box = Hive.box("Student");
snapshot.data[index].postedBy != box.get("ID")
? Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => BookDetail(
book: snapshot.data[index],
),
),
)
: Navigator.push(
context,
MaterialPageRoute(
builder: (con) =>
MyPostedBookDetail(
book: snapshot.data[index],
),
),
);
},
child: ListTile(
title: Text(
snapshot.data[index].bookName.toString(),
),
),
);
},
)
: Column(
crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 50,
),
Center(
child: Text(
"No results found for '$query'",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 50,
),
Center(
child: Text(
"No results found for '$query'",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
),
],
);
// break;
}
// },
);
},
);
return Container();
}
#override
List<Widget> buildActions(BuildContext context) => [];
}

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({})