I believe I am having issues with using setState in a futurebuilder - but Im not sure how to change this? Below you can see I am building a listView using a futureBuilder (http request to api) and connecting it to my ProjectModel model. From here I filter the values I need into List<ProjectSearch> searchList
. What I am finding is when I try to implement a search box to filter through the objects I believe the setState is causing the futurebuilder to rebuild the page each time causing duplications. How can I separate the futurebuilder from the listView so that it only displays the one list object as the user types?
class _HomePageState extends State<HomePage> {
TextEditingController controller = TextEditingController();
late final Future<ProjectModel> futureProjects;
List<ProjectSearch> searchList = [];
List<ProjectSearch> finder = [];
var jobNames = [];
var jobNumbers = [];
var techs = [];
var pms = [];
var address = [];
var majors = [];
var budget = [];
@override
void initState() {
super.initState();
futureProjects = fetchProjects();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
'Upcoming/Live Projects',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
backgroundColor: ColorConstants.darkScaffoldBackgroundColor,
),
drawer: const CustomDrawer(),
backgroundColor: ColorConstants.lightScaffoldBackgroundColor,
body: Center(
child: FutureBuilder<ProjectModel>(
future: futureProjects,
builder: (context, snapshot) {
// finder.clear();
if (snapshot.hasData) {
var data = snapshot.data!;
var columns = data.columns;
var rows = data.rows;
for (var item in rows!) {
var cells = item.cells;
for (var elements in cells!) {
if (elements.columnId != null) {
if (elements.columnId == 2057691532158852) {
var displayValues = elements.displayValue;
if (displayValues != null) {
jobNames.add(displayValues);
}
if (displayValues == null) {
pms.removeLast();
techs.removeLast();
address.removeLast();
majors.removeLast();
budget.removeLast();
}
}
if (elements.columnId == 697505454286724) {
var projectNumber = elements.displayValue;
if (projectNumber != null) {
jobNumbers.add(projectNumber);
}
}
if (elements.columnId == 7452904895342468) {
var techAssigned = elements.displayValue;
if (techAssigned != null) {
if (techAssigned == 'ts@ag.com.au') {
techAssigned = 'Ts';
techs.add(techAssigned);
} else {
techs.add(techAssigned);
}
}
if (techAssigned == null) {
techAssigned = 'No tech assigned as yet';
techs.add(techAssigned);
}
}
if (elements.columnId == 2949305267971972) {
var pmName = elements.displayValue;
if (pmName != null) {
pms.add(pmName);
}
if (pmName == null) {
pmName = 'No project manager allocated';
pms.add(pmName);
}
}
if (elements.columnId == 5201105081657220) {
var addressValue = elements.displayValue;
if (addressValue != null) {
address.add(addressValue);
}
if (addressValue == null) {
addressValue = '';
address.add(addressValue);
}
}
if (elements.columnId == 52961559766916) {
var majorValue = elements.displayValue;
if (majorValue != null) {
majors.add(majorValue);
}
if (majorValue == null) {
majorValue = 'No';
majors.add(majorValue);
}
}
if (elements.columnId == 4226807856686980) {
var budgetHours = elements.displayValue;
if (budgetHours != null) {
budget.add(budgetHours);
}
if (budgetHours == null) {
budgetHours = 'TBA';
budget.add(budgetHours);
}
}
}
}
}
int index = 0;
for (int i = 0; i < jobNames.length; i++) {
// List<ProjectSearch> innerMap = [];
ProjectSearch myProjects = ProjectSearch(
address: jobNames[index],
budget: budget[index],
jobNumber: jobNumbers[index],
major: majors[index],
name: jobNames[index],
pM: pms[index],
tech: techs[index]);
index++;
searchList.add(myProjects);
}
return Container(
child: Column(
children: <Widget>[
Padding(
padding:
const EdgeInsets.only(left: 20, top: 20, right: 20),
child: TextField(
textAlign: TextAlign.center,
controller: controller,
onChanged: search,
keyboardType: const TextInputType.numberWithOptions(
signed: true),
// keeps going....
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: finder.length,
itemBuilder: (context, index) {
// print(finder.length);
final projectData = finder[index];
return MaterialButton(
onPressed: () => showModalBottomSheet<void>(
backgroundColor: Colors.transparent,
context: context,
builder: (BuildContext context) {
return ClipRRect(
borderRadius: const BorderRadius.only(
topRight: Radius.circular(27.0),
topLeft: Radius.circular(27.0)),
child: Container(
height: 1000,
color: ColorConstants
.secondaryDarkAppColor,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: Container(
height: 400,
margin: const EdgeInsets.only(
top: 20,
left: 20,
right: 20),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment
.spaceAround,
children: [
const Padding(
padding:
EdgeInsets.only(
top: 10.0,
bottom: 5.0)),
const Center(
child: Text(
'Project Details',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight:
FontWeight
.w700),
),
),
Row(
children: [
const Text(
'Project: ',
style: TextStyle(
color: Colors
.white70,
fontWeight:
FontWeight
.w600,
fontSize: 17),
),
const SizedBox(
width: 20),
Flexible(
child: Padding(
padding:
const EdgeInsets
.symmetric(
horizontal:
8.0),
child: Text(
projectData.name,
overflow:
TextOverflow
.ellipsis,
maxLines: 2,
style: const TextStyle(
color: Colors
.white,
fontWeight:
FontWeight
.w600,
fontSize: 17),
),
),
),
],
),
],
),
),
);
},
),
child: Container(
height: 50,
margin: const EdgeInsets.only(
top: 30, left: 20, right: 20),
decoration: const BoxDecoration(
color: ColorConstants
.darkScaffoldBackgroundColor,
borderRadius:
BorderRadius.all(Radius.circular(8)),
),
padding: const EdgeInsets.all(15),
child: Center(
child: Text(
projectData.name,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 17),
),
),
),
);
}),
),
],
),
);
} else if (snapshot.hasError) {
print(snapshot);
return Text(
'${snapshot.error}',
style: const TextStyle(color: Colors.white),
);
} else {
return const CircularProgressIndicator();
}
}),
),
);
}
void search(String query) {
final suggestions = searchList.where((search) {
final projectName = search.name.toLowerCase();
final input = query.toLowerCase();
return projectName.contains(input);
}).toList();
setState(() {
finder.clear();
finder = suggestions;
});
}
}
Inside your FutureBuilder's builder, try this:
searchList = [];// <---- add this
int index = 0;
for (int i = 0; i < jobNames.length; i++) {
// List<ProjectSearch> innerMap = [];
ProjectSearch myProjects = ProjectSearch(
address: jobNames[index],
budget: budget[index],
jobNumber: jobNumbers[index],
major: majors[index],
name: jobNames[index],
pM: pms[index],
tech: techs[index]);
index++;
searchList.add(myProjects);
}
and also because when every time keyboard status change your FutureBuilder rebuild again do this:
var data = snapshot.data!;
var columns = data.columns;
var rows = data.rows;
jobNames = [];
jobNumbers = [];
techs = [];
pms = [];
address = [];
majors = [];
budget = [];
for (var item in rows!) {
var cells = item.cells;
for (var elements in cells!) {
...
}
}