I have the below code for fetching updated data on changing the filter value and pressing update. The data is getting fetched, but the list is not getting updated. How to make this work. The list is showing properly once loaded but it is not updating.
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.red,
),
onPressed: () async {
setState(() {
accordianFlag = false;
});
// programsListFuture = getCropNutritionPrograms();
// var updatedProgramsList = await getCropNutritionPrograms();
// setState(() {
// programsListFuture = Future.value(updatedProgramsList);
// });
fetchData(); // <--- not working. I tried various other method, but list is not updating
},
child: Text(
'Update',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold
),
),
)
Full code:
class CropNutritionProgramOverviewScreen extends StatefulWidget {
CropNutritionProgramOverviewScreen({Key? key, required this.showAppbar, required this.showBottombar}) : super(key: key);
bool showAppbar = false;
bool showBottombar = false;
@override
State<CropNutritionProgramOverviewScreen> createState() => _CropNutritionProgramOverviewScreenState();
}
class _CropNutritionProgramOverviewScreenState extends State<CropNutritionProgramOverviewScreen> {
Future<RegionsList?>? regionsListFuture;
Future<CropsModel?>? cropsListFuture;
Future<CropNutritionProgramModel?>? programsListFuture;
bool accordianFlag = false;
bool _showContent = false;
String regionSelected = '1';
String cropSelected = '0';
List<DropdownMenuItem<String>> regionDropdownItems = [];
List<DropdownMenuItem<String>> cropDropdownItems = [];
_CropNutritionProgramOverviewScreenState();
@override
void initState() {
super.initState();
}
Future<RegionsList?> getRegions() async {
RegionsList regionsList = await getRegionsAPI();
print("flutter - regions list: " + regionsList.toString());
List<Region> regions = regionsList.regions ?? [];
for (Region region in regions) {
regionDropdownItems.add(DropdownMenuItem(
child: Text(region.name),
value: region.id.toString(),
));
}
if (regions.length > 0) {
regionSelected = regions[0].id.toString();
}
return regionsList;
}
Future<CropsModel?> getCrops(int regionId) async {
CropsModel cropsList = await getCropsAPI(regionId);
print("flutter - cropsList: " + cropsList.toString());
List<Crop> crops = cropsList.cropsList ?? [];
for (Crop crop in crops) {
cropDropdownItems.add(DropdownMenuItem(
child: Text(crop.name),
value: crop.id.toString(),
));
}
return cropsList;
}
Future<CropNutritionProgramModel?> getCropNutritionPrograms() async {
var regionId = int.parse(regionSelected);
var cropId = int.parse(cropSelected);
CropNutritionProgramModel programsModel = await getCropNutritionProgramsAPI(regionId, cropId);
print("flutter - cropNutritionProgramsModel: " + programsModel.toString());
return programsModel;
}
Future<List<dynamic>> fetchData() async {
return await Future.wait([getRegions(), getCrops(0), getCropNutritionPrograms()]);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: widget.showAppbar ? TopAppBar() : null,
drawer: widget.showAppbar ? TopDrawer() : null,
body: FutureBuilder<List<dynamic>>(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return _buildLoadingIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) {
var regionsList = snapshot.data?.elementAt(0) as RegionsList?;
var cropsList = snapshot.data?.elementAt(1) as CropsModel?;
var programsList = snapshot.data?.elementAt(2) as CropNutritionProgramModel?;
List<Region> regions = regionsList == null ? [] : regionsList!.regions;
List<Crop> crops = cropsList == null ? [] : cropsList!.cropsList;
List<CropNutritionProgram> programs = programsList == null ? [] : programsList.cropNutritionPrograms;
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 16, left: 16, bottom: 8),
child: Text(
'Crop Nutrition Programs',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
),
),
Expanded(
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 4.0),
Image.asset(
regionsAssetPath,
fit: BoxFit.cover,
),
SizedBox(height: 16.0),
Center(
child: Text(
"Discover Crop Nutrition Program recommendations for your state!",
textAlign: TextAlign.center,
),
),
SizedBox(height: 16.0),
Padding(
padding: const EdgeInsets.only(left: 16.0, right: 16.0, top: 0.0, bottom: 8.0),
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Card(
shape: new RoundedRectangleBorder(
side: new BorderSide(color: Colors.red, width: 1.0),
borderRadius: BorderRadius.circular(4.0),
),
child: Column(
children: [
Container(
height: 40,
child: Transform.translate(
offset: Offset(0, -8),
child: ListTile(
title: const Text("Filter By", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16, color: Colors.red)),
leading: Icon(Icons.filter_alt_outlined, color: Colors.red,),
trailing: IconButton(
icon: Icon(
_showContent ? Icons.arrow_drop_up : Icons.arrow_drop_down, color: Colors.red,),
onPressed: () {
setState(() {
_showContent = !_showContent;
});
},
),
onTap: () {
setState(() {
_showContent = !_showContent;
});
},
),
),
decoration: new BoxDecoration(
border: new Border(bottom: new BorderSide(color: Colors.red)),
),
),
_showContent
? Container(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 5,
),
Padding(
padding:
const EdgeInsets.only(left: GlobalMargin.leftAccordian, right: GlobalMargin.rightAccordian),
child: InputDecorator(
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 0),
border: OutlineInputBorder(),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: regionSelected,
isExpanded: true,
isDense: true,
hint: Text('Select Region'),
icon:
const Icon(Icons.arrow_drop_down_outlined),
style: const TextStyle(color: Colors.black),
onChanged: (value) {
setState(() {
accordianFlag = true;
if (value != null) {
regionSelected = value;
}
});
},
items: regionDropdownItems
),
),
),
),
SizedBox(
height: 10,
),
Padding(
padding:
const EdgeInsets.only(left: GlobalMargin.leftAccordian, right: GlobalMargin.rightAccordian),
child: InputDecorator(
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 0),
border: OutlineInputBorder(),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: cropSelected,
isExpanded: true,
isDense: true,
hint: Text('Select Crop'),
icon:
const Icon(Icons.arrow_drop_down_outlined),
style: const TextStyle(color: Colors.black),
onChanged: (value) {
setState(() {
accordianFlag = true;
if (value != null) {
cropSelected = value;
}
});
},
items: cropDropdownItems,
),
),
),
),
SizedBox(
height: 10,
),
Container(
width: double.infinity,
alignment: Alignment.topCenter,
child: Container(
width: 200,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.red,
),
onPressed: () async {
setState(() {
accordianFlag = false;
});
// programsListFuture = getCropNutritionPrograms();
// var updatedProgramsList = await getCropNutritionPrograms();
// setState(() {
// programsListFuture = Future.value(updatedProgramsList);
// });
fetchData();
},
child: Text(
'Update',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold
),
),
)
)
),
],
),
)
: Container(),
],
),
);
},
),
),
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: programs.length,
separatorBuilder: (_, __) => Divider(height: 1),
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.fromLTRB(-8, 8, 0, 8),
child: ListTile(
title: Text("${programs[index].name}"),
trailing: Icon(Icons.arrow_forward, color: Colors.red),
onTap: () {
print("crop nutrition program tapped");
navigateToCropNutritionListScreen(context, int.parse(regionSelected), 0);
},
),
);
}
),
]
),
),
),
),
],
),
);
} else {
return Text('No data available');
}
},
),
);
}
void navigateToCropNutritionListScreen(BuildContext context, int regionId, int stateId) {
var provider = Provider.of<AppBarContitonProvider>(
context,
listen: false,
);
provider.setInnerPageLoaded(true);
provider.setPdfScreenLoaded(false, '');
NavKey.key.currentState?.pushNamed(Routes.cropNutritionList,
arguments: CropNutritionListArguments(regionId, stateId)).then((value) {
});
}
Widget _buildLoadingIndicator() {
return Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child: CircularProgressIndicator(),
),
);
}
}
I have updated the code to use only one future variable but still the list is not rebuilding when new data is fetched.
Code: https://pastebin.com/AYENDAkr
Could you please help me with this? Thanks.
I am new to flutter. I don’t understand why when a variable changes the corresponding widget does not update.
programsListFuture = getCropNutritionPrograms();
var updatedProgramsList = await getCropNutritionPrograms();
setState(() {
programsListFuture = Future.value(updatedProgramsList);
});
The future is assigned new value, but why is the list not updated?
2
Answers
try and reorganize the way you initialize the app, using the future builder like this and creating the future in itself is an error, start by working this out before moving to your described bug, see the flutter youtube channel on this take https://www.youtube.com/watch?v=zEdw_1B7JHY
If you want to continuously monitor the changes of a certain attribute, you can use
ValueListenableBuilder
andValueNotifier
to achieve that.ValueListenableBuilder DartPad
You can run this DartPad to see the effect.