I'm trying to search a list that I get from GET request based on the parameter that I pass from my TextField controller but the search isn't working, it doesn't display any result and it doesn't show any error either.
Can you please help me figure out what I'm doing wrong?
API request
static Future<List<Athlete>> searchAthletesByName(controller, context) async {
try {
final response = await http.get(
Uri.parse(
'$uri/search-athletes-by-name?name=$controller&page=0&length=50'),
headers: {
'Authorization': 'Basic ..',
'Content-Type': 'application/json',
'Accept': 'application/json'
});
if (response.statusCode == 200) {
List jsonResponse = json.decode(utf8.decode(response.bodyBytes));
return jsonResponse
.map((_athlete) => Athlete.fromJson(_athlete))
.toList();
}
} catch (e) {
logger.e(e.toString());
}
return searchAthletesByName(controller, context);
}
The screen where I want to implement the search
class _AddAthleteState extends State<AddAthlete> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
TextEditingController myController = TextEditingController();
Future<List<Athlete>>? futureSearchAthleteByName;
late List<Athlete> _athlete = [];
@override
void initState() {
futureSearchAthleteByName =
ApiService.searchAthletesByName(myController, context);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: SingleChildScrollView(
child: Column(
children: [
Align(
alignment: Alignment.topCenter,
child: Container(
margin: const EdgeInsets.only(
left: 10, right: 10, top: 10, bottom: 5),
child: TextFormField(
onChanged: (value) {
setState(() {});
},
controller: myController,
),
),
),
Stack(
children: [
SingleChildScrollView(
child: Column(children: [
const SizedBox(
height: 10,
),
const SizedBox(
height: 10,
),
FutureBuilder<List<Athlete>>(
future: futureSearchAthleteByName,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<Athlete> _athlete = snapshot.data;
return ListView.builder(
shrinkWrap: true,
cacheExtent: 34,
primary: true,
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(
top: 10,
bottom: 56,
),
itemCount: _athlete.length,
itemBuilder: (BuildContext context, int index) {
if (myController.text == '') {
return Container();
} else if (myController.text != '' &&
_athlete[index]
.lastName
.toLowerCase()
.contains(myController.text
.toLowerCase()) ||
_athlete[index]
.firstName
.toLowerCase()
.contains(
myController.text.toLowerCase())) {
return Column(
children: [
ListTile(
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: Text(
'${_athlete[index].lastName} ${_athlete[index].firstName}',
});
} else if (snapshot.hasError) {
logger.e('${snapshot.error}');
}
return Container();
CodePudding user response:
You are passing the controller object to the api endpoint. You should pass the controller.text
static Future<List<Athlete>> searchAthletesByName(controller, context) async {
try {
final response = await http.get(
Uri.parse(
'$uri/search-athletes-by-name?name=${controller.text}&page=0&length=50'),
headers: {
'Authorization': 'Basic ..',
'Content-Type': 'application/json',
'Accept': 'application/json'
});
if (response.statusCode == 200) {
List jsonResponse = json.decode(utf8.decode(response.bodyBytes));
return jsonResponse
.map((_athlete) => Athlete.fromJson(_athlete))
.toList();
}
} catch (e) {
logger.e(e.toString());
}
return searchAthletesByName(controller, context);}
CodePudding user response:
Since you rely on rebuilding the FutureBuilder
based on the TextFormField
widget, use this :
@override
void initState() {
futureSearchAthleteByName =
ApiService.searchAthletesByName(myController, context);
super.initState();
}
will prevent the FutureBuilder
to rebuild on any setState((){})
, so you need to put the Future
method directly in the future property of the FutureBuilder
:
FutureBuilder<List<Athlete>>(
future: ApiService.searchAthletesByName(myController, context),
/* more code */
now when SetState(() {})
is triggered the FutureBuilder
will rebuild, in other words, it will send a new request to get a new snapshot.data
ans show it.
the myController is initialize with no value so the text
is null, that's why it can throw an error trying to fetch for first time.
you can initialize the controller with an empty text like this :
TextEditingController myController = TextEditingController();
with this :
TextEditingController myController = TextEditingController(text = "");
Here you can notice the second thing, that your passing a TextEditingController
object which is myController
inside your method, then you're using that controller in the method directly, which doesn't set the request based on the String
value of TextFormField
, you can get it with the text
property onn the myController
, so instead of this :
Uri.parse( '$uri/search-athletes-by-name?name=$controller&page=0&length=50'),
replace with this :
Uri.parse( '$uri/search-athletes-by-name?name=${controller.text}&page=0&length=50'),