'Flutter future json list define as a final and stop updating it
I could fetch the JSON response from the server and make a list. Now I wanted to add a filter to that list.
To do so, I followed an online tutorial. in that tutorial, the "duplicateItems" variable has created as final. See the code:
final duplicateItems = List<String>.generate(10000, (i) => "Item $i");
var items = List<String>();
but in my case, as I'm using Future method to get the list from a server, I can't or I don't know to make that variable as final. See the code:
class _ListServiceProvidersState extends State<ListServiceProviders> {
List items;
List duplicateItems;
@override
void initState() {
super.initState();
getSharedValues();
}
getSharedValues() async {
bool value = await sharedPreferenceService.getSharedPreferencesInstance();
if (value) {
token = await sharedPreferenceService.token;
fetchServices();
} else {
commonModelServices.showMessage(
'You must log in before use the services', _scaffoldKey, Colors.red);
Navigator.pushNamed(
context,
'/LoginPage',
);
}
}
Future<String> fetchServices() async {
SchedulerBinding.instance.addPostFrameCallback((_) {
commonModelServices.onLoading(context);
});
final response = await http.get(
'API URL?category_id=$catId&city=$cityId&latitude=$latitude&longitude=$longitude');
setState(() {
commonModelServices.offLoading(context);
var resBody = json.decode(response.body);
if (resBody['success'] == true) {
setState(() {
duplicateItems = resBody['data']['data'];
items = duplicateItems;
});
}
});
return "Success";
}
This is my filtering function
void filterSearchResults(String query) {
List dummySearchList = List();
print('Original List');
dummySearchList.addAll(duplicateItems);
print(dummySearchList);
print('Search Query');
print(query);
if (query.isNotEmpty) {
print('search query not empty');
List dummyListData = List();
print('Start of the loop');
dummySearchList.forEach((item) {
print('List single item');
print(item);
if (item['name'].contains(query)) {
print('Item contain search query');
dummyListData.add(item);
}
});
print('End of the loop');
setState(() {
print('Clear duplicated List');
items.clear();
print('Set searched results');
items.addAll(dummyListData);
});
return;
} else {
print('Search query empty');
setState(() {
print('Clear prevoius searched in duplicate list');
items.clear();
print('Add Original List to duplicate');
items.addAll(duplicateItems);
});
}
}
The filter works as expected for the first letter in the search field. but then for the next key press, when the filterSearchResults function execute, the original List also get updated to the searched result of the first key press. So it search the search term from the previous searched results but not from the Original List. So, the result is wrong.
I want to keep the original Variable "duplicateItems" unchanged after a search.
Can you tell me what is missing here?
For your information, I'll put the full code below.
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_healthcare_app/src/theme/extention.dart';
import 'package:flutter_healthcare_app/src/theme/light_color.dart';
import 'package:flutter_healthcare_app/src/theme/text_styles.dart';
import 'package:flutter_healthcare_app/src/theme/theme.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_healthcare_app/src/model/shared_pref_model.dart';
import 'package:flutter_healthcare_app/src/model/common_model.dart';
class ListServiceProviders extends StatefulWidget {
ListServiceProviders({Key key}) : super(key: key);
@override
_ListServiceProvidersState createState() => _ListServiceProvidersState();
}
class _ListServiceProvidersState extends State<ListServiceProviders> {
TextEditingController editingController = TextEditingController();
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
SharedPreferenceService sharedPreferenceService = SharedPreferenceService();
CommonModel commonModelServices = CommonModel();
List items;
List duplicateItems;
String token;
String cityId;
int catId;
String latitude;
String longitude;
@override
void initState() {
super.initState();
getSharedValues();
}
getSharedValues() async {
bool value = await sharedPreferenceService.getSharedPreferencesInstance();
if (value) {
token = await sharedPreferenceService.token;
cityId = await sharedPreferenceService.cityId;
catId = await sharedPreferenceService.catId;
latitude = await sharedPreferenceService.latitude;
longitude = await sharedPreferenceService.longitude;
fetchServices();
} else {
commonModelServices.showMessage(
'You must log in before use the services', _scaffoldKey, Colors.red);
Navigator.pushNamed(
context,
'/LoginPage',
);
}
}
Future<String> fetchServices() async {
SchedulerBinding.instance.addPostFrameCallback((_) {
commonModelServices.onLoading(context);
});
final response = await http.get(
'API URL?category_id=$catId&city=$cityId&latitude=$latitude&longitude=$longitude');
setState(() {
commonModelServices.offLoading(context);
var resBody = json.decode(response.body);
if (resBody['success'] == true) {
setState(() {
duplicateItems = resBody['data']['data'];
items = duplicateItems;
});
}
});
return "Success";
}
void filterSearchResults(String query) {
List dummySearchList = List();
print('Original List');
dummySearchList.addAll(duplicateItems);
print(dummySearchList);
print('Search Query');
print(query);
if (query.isNotEmpty) {
print('search query not empty');
List dummyListData = List();
print('Start of the loop');
dummySearchList.forEach((item) {
print('List single item');
print(item);
if (item['name'].contains(query)) {
print('Item contain search query');
dummyListData.add(item);
}
});
print('End of the loop');
setState(() {
print('Clear duplicated List');
items.clear();
print('Set searched results');
items.addAll(dummyListData);
});
return;
} else {
print('Search query empty');
setState(() {
print('Clear prevoius searched in duplicate list');
items.clear();
print('Add Original List to duplicate');
items.addAll(duplicateItems);
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: commonModelServices.appBar(context),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
child: _header(),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (value) {
filterSearchResults(value);
},
controller: editingController,
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0)))),
),
),
Container(
height: AppTheme.fullHeight(context),
child: new ListView.builder(
itemCount: items == null ? 0 : items.length,
itemBuilder: (BuildContext context, int index) {
return _categoryListView(index, items, context);
})),
],
)));
}
Widget _header() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Categories", style: TextStyles.titleM),
],
).p16;
}
/* Widget _doctorsList(index, data, context) {
}*/
Widget _categoryListView(index, model, context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(20)),
boxShadow: <BoxShadow>[
BoxShadow(
offset: Offset(4, 4),
blurRadius: 10,
color: LightColor.grey.withOpacity(.2),
),
BoxShadow(
offset: Offset(-3, 0),
blurRadius: 15,
color: LightColor.grey.withOpacity(.1),
)
],
),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 18, vertical: 8),
child: ListTile(
contentPadding: EdgeInsets.all(0),
leading: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(13)),
child: Container(
height: 55,
width: 55,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: randomColor(),
image: DecorationImage(
image: NetworkImage(model[index]['image'].toString()),
fit: BoxFit.cover,
),
),
/*child: Image.asset(
model[index]['image'].toString(),
height: 50,
width: 50,
fit: BoxFit.contain,
),*/
),
),
title: Text(model[index]['name'], style: TextStyles.title.bold),
subtitle: Text(
model[index]['description'],
style: TextStyles.bodySm.subTitleColor.bold,
),
trailing: Icon(
Icons.keyboard_arrow_right,
size: 30,
color: Theme.of(context).primaryColor,
),
),
).ripple(() {
Navigator.pushNamed(context, "/DetailPage", arguments: model);
}, borderRadius: BorderRadius.all(Radius.circular(20))),
);
}
void _showCategoryListPage(selectedCategory, catId, context) {
sharedPreferenceService.setCatId(catId);
Navigator.pushNamed(
context,
'/SelectServiceProvider',
);
}
Color randomColor() {
var random = Random();
final colorList = [
Theme.of(context).primaryColor,
LightColor.orange,
LightColor.green,
LightColor.grey,
LightColor.lightOrange,
LightColor.skyBlue,
LightColor.titleTextColor,
Colors.red,
Colors.brown,
LightColor.purpleExtraLight,
LightColor.skyBlue,
];
var color = colorList[random.nextInt(colorList.length)];
return color;
}
}
Solution 1:[1]
I got the answer in the google flutter community. I'm sharing it here for the future use of someone else.
there is only one line to update.
As I just do this
setState(() {
duplicateItems = resBody['data']['data'];
items = duplicateItems;
});
it only makes a reference variable. So when I update the items variable it automatically updates the duplicateItems variable as well.
so I have to update that like below. So both are working as two different lists.
duplicateItems = List.from(items);
So full code is like below.
setState(() {
commonModelServices.offLoading(context);
var resBody = json.decode(response.body);
if (resBody['success'] == true) {
setState(() {
items = resBody['data']['data'];
});
}
//items = duplicateItems;
duplicateItems = List.from(items);
});
Solution 2:[2]
The same thing was happening to me as to you. Matching variables is referencing them with each other, not copying their data. Thank you very much for posting your solution.
I had
items = jsonResponse.data;
duplicateItems = items;
That references the items variable but doesn't copy the data.
What is correct is what you say
items = jsonResponse.data;
duplicateItems = List.from(items);
So the filter works.
The original tutorial is
https://karthikponnam.medium.com/flutter-search-in-listview-1ffa40956685
Thank you very much for posting your solution. Solution elementary dear Watson
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Yasitha |
Solution 2 | siemensgti |