skip to Main Content

I am building a bottle delivery app using Flutter. I am having issues searching and mapping from my List of bottle categories. I am failing to search for any products in the search bar. I know the problem has to do with the fact that the list I am searching from is a different list from the one that was used to map values into the widget. I have been struggling for a while with this problem. How do I ensure I am searching and mapping from the same list? Can anyone please kindly assist. Here’s a image of the app in web

Before Search
enter image description here
After Search (As you can see nothing appears when search happens)
enter image description here
Here’s my code:

class Bottle {
  String id;
  String bottleName;
  String image;
  String bottletype;
  int time;
  double price;
  Bottle(
      {required this.id,
      required this.bottleName,
      required this.image,
      required this.bottletype,
      required this.time,
      required this.price});
}

class BottleCategory {
  int categoryNo;
  String bottleType;
  List<Bottle> bottleList;
  BottleCategory(
      {required this.categoryNo,
      required this.bottleType,
      required this.bottleList});
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
  ProductProvider productProvider = ProductProvider();
  late TabController tabController;
  late Bottle bottleProvider;

  final List<BottleCategory> categories = [
    BottleCategory(categoryNo: 1, bottleType: 'Brandy', bottleList: [
      Bottle(
          id: 'KLPB',
          bottleName: 'Klipdrift Brandy',
          image: 'lib/assets/images/KlipdriftBrandy.png',
          time: 20,
          price: 274.00,
          bottletype: 'Brandy'),
      Bottle(
          id: 'KWV3',
          bottleName: 'Kwv Brandy 3 years',
          image: 'lib/assets/images/KwvBrandy3years.jpg',
          time: 20,
          price: 274.00,
          bottletype: 'Brandy'),
    ]),
    BottleCategory(categoryNo: 2, bottleType: 'Gin', bottleList: [
      Bottle(
          id: 'BMBS',
          bottleName: 'Bombay Sapphire Gin',
          image: 'lib/assets/images/BombaySapphireGin.png',
          time: 20,
          price: 274.00,
          bottletype: 'Gin'),
      Bottle(
          id: 'TNGN',
          bottleName: 'Tanqueray Gin',
          image: 'lib/assets/images/TanquerayGin.png',
          time: 20,
          price: 274.00,
          bottletype: 'Gin'),
    ]),
    BottleCategory(categoryNo: 3, bottleType: 'Soft Drinks', bottleList: [
      Bottle(
          id: 'COCA',
          bottleName: 'Coca Cola',
          image: 'lib/assets/images/CocaCola.jpg',
          time: 20,
          price: 274.00,
          bottletype: 'Soft Drinks'),
      Bottle(
          id: 'SPRT',
          bottleName: 'Sprite',
          image: 'lib/assets/images/Sprite.jpg',
          time: 20,
          price: 274.00,
          bottletype: 'Soft Drinks'),
    ]),
    BottleCategory(categoryNo: 4, bottleType: 'Whiskey', bottleList: [
      Bottle(
          id: '',
          bottleName: 'Jameson',
          image: 'lib/assets/images/Jameson.jpg',
          time: 20,
          price: 274.00,
          bottletype: 'Whiskey'),
      Bottle(
          id: '',
          bottleName: 'Johnnie Walker Black Label',
          image: 'lib/assets/images/JohnnieWalkerBlackLabel.jpg',
          time: 20,
          price: 274.00,
          bottletype: 'Whiskey'),
    ]),
  ];

  List<Bottle> getBottleItem() {
    List<Bottle> localBottles = [];
    for (var x = 0; x < categories.length; x++) {
      var currentElement = categories[x].bottleList;
      for (var y = 0; y < currentElement.length; y++) {
        var bottleElement = currentElement[y];
        localBottles.add(bottleElement);
      }
    }
    print(localBottles);
    return localBottles;
  }

  late List<Bottle> allBottles = getBottleItem();
  late List<Bottle> bottles;
  String query = " ";

  void searchBottle(String query) {
    final bottles = allBottles.where((bottle) {
      final bottleNameLower = bottle.bottleName.toLowerCase();
      final searchLower = query.toLowerCase();

      return bottleNameLower.contains(searchLower);
    }).toList();

    setState(() {
      this.bottles = bottles;
      this.query = query;
    });
  }

  Widget buildSearch() {
    return SearchWidget(
      text: query,
      hintText: 'Search for beverages',
      onChanged: searchBottle,
    );
  }

  @override
  void initState() {
    super.initState();
    tabController = TabController(length: 4, vsync: this);
  }

  @override
  void dispose() {
    tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    var cart = Provider.of<ShoppingCartProvider>(context);
    Size _screenSize = MediaQuery.of(context).size;
    final double itemHeight = (_screenSize.height - kToolbarHeight - 24) / 2;
    final double itemWidth = _screenSize.width / 2;
    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            buildSearch(),
            const Padding(
              padding: EdgeInsets.all(8.0),
              child: Text(
                'Categories',
                style: TextStyle(
                    fontSize: 20.0,
                    fontFamily: 'Montserrat-ExtraBold',
                    fontWeight: FontWeight.bold),
              ),
            ),
            Container(
              child: Align(
                alignment: Alignment.centerLeft,
                child: TabBar(
                  controller: tabController,
                  indicator:
                      CircleTabIndicator(color: Colors.redAccent, radius: 4.0),
                  isScrollable: true,
                  labelColor: Colors.redAccent,
                  labelStyle: const TextStyle(
                      fontWeight: FontWeight.bold, fontSize: 20.0),
                  unselectedLabelColor: Colors.black,
                  unselectedLabelStyle: const TextStyle(
                      fontWeight: FontWeight.bold, fontSize: 20.0),
                  tabs: const [
                    Tab(text: 'Brandy'),
                    Tab(text: 'Gin'),
                    Tab(text: 'Soft drinks'),
                    Tab(text: 'Whiskey')
                  ],
                ),
              ),
            ),
            Container(
              height: 400,
              width: double.maxFinite,
              child: TabBarView(
                  controller: tabController,
                  children: categories.map((bottleCategory) {
                    return GridView.count(
                        crossAxisCount: 2,
                        childAspectRatio: itemWidth / itemHeight,
                        children: bottleCategory.bottleList.map((bottle) {
                          return ProductItem(
                            id: bottle.id,
                            bottleName: bottle.bottleName,
                            imgUrl: bottle.image,
                            price: bottle.price,
                          );
                        }).toList());
                  }).toList()),
            ),
          ],
        ),
      ),
    );
  }
}

class SearchWidget extends StatefulWidget {
  final String text;
  final ValueChanged<String> onChanged;
  final String hintText;
  const SearchWidget(
      {Key? key,
      required this.text,
      required this.onChanged,
      required this.hintText})
      : super(key: key);

  @override
  State<SearchWidget> createState() => _SearchWidgetState();
}

    class _SearchWidgetState extends State<SearchWidget> {
      final controller = TextEditingController();
      @override
      Widget build(BuildContext context) {
        return Container(
          child: TextField(
            controller: controller,
            decoration: InputDecoration(
                icon: const Icon(
                  Icons.search,
                  color: Colors.grey,
                ),
                suffixIcon: widget.text.isNotEmpty
                    ? GestureDetector(
                        child: const Icon(Icons.close),
                        onTap: () {
                          controller.clear();
                          widget.onChanged('');
                          FocusScope.of(context).requestFocus(FocusNode());
                        },
                      )
                    : null,
                hintText: widget.hintText,
                border: InputBorder.none),
            onChanged: widget.onChanged,
          ),
        );
      }
    }

2

Answers


  1. First, instead of creating a new List<Bottle> allBottles, you can expand your categories to get the list of all Bottles like this :

    final bottles = categories.expand((element) => element.bottleList).toList();
    

    Then, in your GridView, you can directly use the expanded list and apply your query string :

    GridView.count(
      crossAxisCount: 2,
      childAspectRatio: itemWidth / itemHeight,
      children: categories.expand((element) => element.bottleList).toList().where((element) { //Get the flatten list of bottles
        return element.bottleName.toLowerCase().contains(query.toLowerCase()); // Apply the query string
      }).map((bottle) {
        return ProductItem(
          id: bottle.id,
          bottleName: bottle.bottleName,
          imgUrl: bottle.image,
          price: bottle.price,
        );
      }).toList(),
    );
    

    Hope this helps

    Login or Signup to reply.
  2. your setState() call is changing the bottles variable but your list is displayed by the categories variable.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search