I have managed to load a list of cryptocurrencies from an API. This is done via the ListView.builder.
Subsequently, how does one perform a search/filter in order to select an item from the list?
By scrolling towards the end to see the last code, I have shown the code that I presumed would be able to do the job of 'search'. But I am unsure where to place this code.
Image below shows current crypto list loaded from API:
The code for the above screen is as follows:
class AddCryptoAssetScreen extends StatefulWidget {
@override
_AddCryptoAssetScreenState createState() => _AddCryptoAssetScreenState();
}
class _AddCryptoAssetScreenState extends State<AddCryptoAssetScreen> {
Future<List<Asset>> fetchCoin() async {
assetList = [];
final response = await http.get(Uri.parse(
'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false'));
if (response.statusCode == 200) {
List<dynamic> values = [];
values = json.decode(response.body);
if (values.length > 0) {
for (int i = 0; i < values.length; i ) {
if (values[i] != null) {
Map<String, dynamic> map = values[i];
assetList.add(Asset.fromJson(map));
}
}
setState(() {
assetList;
});
}
return assetList;
} else {
throw Exception('Failed to load coins');
}
}
@override
void initState() {
fetchCoin();
Timer.periodic(Duration(seconds: 1), (timer) => fetchCoin());
super.initState();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => Navigator.pop(context),
child: DraggableScrollableSheet(
builder: (_, controller) => Container(
decoration: BoxDecoration(),
clipBehavior: Clip.antiAlias,
child: Scaffold(
appBar: AppBar(),
body: Column(
children: [
Container(
margin: const EdgeInsets.fromLTRB(),
child: TextField(
keyboardType: TextInputType.text,
textAlign: TextAlign.start,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
hintText: 'Search',
border: OutlineInputBorder(),
contentPadding: EdgeInsets.only()),
),
),
Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: assetList.length,
itemBuilder: (context, index) {
return AssetCryptoCard(
name: assetList[index].name,
image: assetList[index].image,
);
},
),
),
],
),
),
),
),
);
}
}
The Asset class is as follows. To derive assetList.
class Asset {
String name;
String image;
num currentPrice;
num priceChange24h;
num priceChangePercentage24h;
String symbol;
Asset({
required this.name,
required this.image,
required this.currentPrice,
required this.priceChange24h,
required this.priceChangePercentage24h,
required this.symbol,
});
factory Asset.fromJson(Map<String, dynamic> json) {
return Asset(
name: json['name'],
symbol: json['symbol'],
image: json['image'],
currentPrice: json['current_price'],
priceChange24h: json['price_change_24h'],
priceChangePercentage24h: json['price_change_percentage_24h'],
);
}
}
List<Asset> assetList = [];
The AssetCryptoCard class is as follows.
class AssetCryptoCard extends StatelessWidget {
AssetCryptoCard({
required this.name,
required this.image,
});
final String name;
final String image;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
showModalBottomSheet(
context: context,
builder: (context) => EditAssetScreen(),
);
},
child: Container(
padding: EdgeInsets.only(),
child: Column(
children: [
Row(
children: [
Container(child: Image.network(image)),
SizedBox(),
Text(name),
Spacer(),
Icon(Icons.arrow_forward_ios_rounded),
],
),
Container(),
],
),
),
);
}
}
I have written the code below but unsure where is the right place to put it. Also, presumably this is the right code to do a search/filter on the list.
List<Map<String, dynamic>> foundAssetList = [];
@override
initState() {
foundAssetList = assetList;
super.initState();
}
void _runFilter(String enteredKeyword) {
List<Map<String, dynamic>> results = [];
if (enteredKeyword.isEmpty) {
results = assetList;
} else {
results = assetList
.where((user) =>
user["name"].toLowerCase().contains(
enteredKeyword.toLowerCase().toList();
}
setState(() {
foundAssetList = results;
});
}
Any help would be much appreciated.
CodePudding user response:
Use the textfoemfield onChanged property
TextFormField(
OnChanged: (value){
_runFilter(value);
}
);
Or
For API request use future feature in textfield_search package
please review this package can provide solution with different approach
I hope it works for you.
CodePudding user response:
You're missing a few things:
- You need a state that keeps track of the text in the
TextField
- you can do this by adding a state variable and useonChanged
of theTextField
to update the variable - Write a function that returns a list of the cryptocurrencies based on the keyword: You can create a function that returns the list of all cryptocurrencies if the keyword is empty, else return a filtered list
- Replace the
assetList
variable in theListView.builder
with thefilteredList
(should be the value of the function in step 2)
The result code should look like this (put this in _AddCryptoAssetScreenState
:
String _keyword = "";
List<Map<String, dynamic>> _getFilteredList() {
if (_keyword.isEmpty) {
return assetList;
}
return assetList
.where((user) =>
user["name"].toLowerCase().contains(
_keyword.toLowerCase())).toList();
}
@override
Widget build(BuildContext context) {
final filteredList = _getFilteredList();
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => Navigator.pop(context),
child: DraggableScrollableSheet(
builder: (_, controller) => Container(
decoration: BoxDecoration(),
clipBehavior: Clip.antiAlias,
child: Scaffold(
appBar: AppBar(),
body: Column(
children: [
Container(
margin: const EdgeInsets.fromLTRB(),
child: TextField(
keyboardType: TextInputType.text,
textAlign: TextAlign.start,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
hintText: 'Search',
border: OutlineInputBorder(),
contentPadding: EdgeInsets.only()),
onChanged: (text) {
setState(() {
_keyword = text;
});
},
),
),
Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: filteredList.length,
itemBuilder: (context, index) {
return AssetCryptoCard(
name: filteredList[index].name,
image: filteredList[index].image,
);
},
),
),
],
),
),
),
),
);
}
Let me know if this works.