Home > Software engineering >  I have a problem with the search implementation
I have a problem with the search implementation

Time:10-04

in this code a have a search function but when ever I search for apple its shows me apple a few times instead of only once

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;
  MyHomePage({Key key, @required this.title}) : super(key: key);



  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  //Search TextField Controller
  final _searchController = TextEditingController();
  
  List<Fruit> mainList = [
    Fruit(name: 'Apple', imageUrl: 'https://images.pexels.com/photos/102104/pexels-photo-102104.jpeg'),
    Fruit(name: 'Banana', imageUrl: 'https://images.pexels.com/photos/5945848/pexels-photo-5945848.jpeg'),
    Fruit(name: 'Pineapple', imageUrl: 'https://images.pexels.com/photos/1071878/pexels-photo-1071878.jpeg'),
    Fruit(name: 'Mango', imageUrl: 'https://images.pexels.com/photos/918643/pexels-photo-918643.jpeg'),
  ];
  List<Fruit> searchList = [];
  

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column( 
        children: [
          Container(
            width: MediaQuery.of(context).size.width,
            height: 60.0,
            child: TextFormField(
              controller: _searchController,
              onChanged: (text){
                final String queryString = _searchController.text;
                setState((){
                  if(queryString.isNotEmpty){
                    
                    for(final fruit in mainList){
                      if(fruit.name.contains(queryString)){
                        searchList.add(fruit);
                      } else{
                        searchList.remove(fruit);
                      }
                    }
                  }else{
                    searchList.clear();
                  }
                });
              }
            ),
          ),
          
          Expanded(
            child: _searchController.text.isEmpty
            ? GridView.count(
              crossAxisCount: 2,
              children: mainList.map((fruit)=> CardWidget(fruit: fruit)).toList(),
            )
            :GridView.count(
              crossAxisCount: 2,
              children: searchList.map((searchedFruit)=>CardWidget(fruit: searchedFruit)).toList()
            ),
          ),
        ],
      ), 
    );
  }
}

I think that the main problem here is with the searchList, It just add the same fruit over and over.

Class to hold Fruit

class Fruit{
  final String imageUrl;
  final String name;
  
  Fruit({this.imageUrl, this.name});
}

widget to be built for each fruit object found in the mainList

//Card Widget
class CardWidget extends StatefulWidget{
  final Fruit fruit;
  
  CardWidget({this.fruit});
  
  @override
  _CardWidgetState createState()=> _CardWidgetState();
}

class _CardWidgetState extends State<CardWidget>{
  
  @override
  Widget build(BuildContext context){
    return Container(
      width: 100.0,
      height: 140.0,
      child: Column(
        children:[
          Image(image: NetworkImage(widget.fruit.imageUrl)),
          SizedBox(height: 10.0),
          Text(widget.fruit.name),
        ]
      )
    );
  }
}

CodePudding user response:

Ok, I believe the problem is every time you enter a new character the code will search and add to the search list, but you have to reset the list for each search otherwise the list will have repeated characters. Here's how to do it:

              onChanged: (text){
                final String queryString = _searchController.text;
                setState((){
                  if(queryString.isNotEmpty){

                    searchList.clear(); // clear the list before the search loop
                    for(final fruit in mainList){
                      if(fruit.name.contains(queryString)){
                        searchList.add(fruit);
                      } else{
                        searchList.remove(fruit);
                      }
                    }
                  }else{
                    searchList.clear();
                  }
                }

CodePudding user response:

try this..

initialize debounce below the TextEditingController

final _searchController = TextEditingController();
final _deBouncer = Debouncer(millisecound: 400);

replace onChanged function

onChanged: (text) async {
              searchList.clear();
              await _deBouncer.run(() {
                final String queryString = text;
                if (queryString.isNotEmpty) {
                  for (final fruit in mainList) {
                    if 
 (fruit.name?.toLowerCase().contains(queryString.toLowerCase()) ?? 
 false) {
                      searchList.add(fruit);
                    }
                  }
                }
                setState(() {});
              });
            }),

add this class for when user stop to typing in search text field

class Debouncer {
 final int? millisecound;
 VoidCallback? action;
 Timer? _timer;

 Debouncer({this.millisecound});

 run(VoidCallback action) {
  if (_timer != null) {
  _timer?.cancel();
  }
  _timer = Timer(Duration(milliseconds: millisecound ?? 300), action);
  }
 }
  • Related