Home > Software engineering >  Search a list based on the input in API in Flutter
Search a list based on the input in API in Flutter

Time:11-02

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'),
     
  • Related