1

hi all I'm trying to create a search bar but I can't I'm quite new I have a list in a CartModel class that I display in a FruitsPage class but I can't filter the search result.

CartModel

class CartModel extends ChangeNotifier {
final List _shopItems = const [
// [ itemName, itemPrice, imagePath, color ]
["Avocado", "4", "assets/images/8c.png", Colors.green],
["Banana", "2", "assets/images/8c.png", Colors.yellow],
["pollo", "12", "assets/images/8c.png", Colors.brown],
["acqua", "1", "assets/images/8c.png", Colors.blue],
["mela", "3", "assets/images/8c.png", Colors.red],
["broccoli", "3", "assets/images/8c.png", Colors.brown],
];
get shopItems => _shopItems;

GroceryItemTile

class GroceryItemTile extends StatelessWidget {
  final String itemName;
  final int itemPrice;
  final String imagePath;
  // ignore: prefer_typing_uninitialized_variables
  final color;
  void Function()? onPressed;

  GroceryItemTile({
    super.key,
    required this.itemName,
    required this.itemPrice,
    required this.imagePath,
    required this.color,
    required this.onPressed,
  });

FruitsPage

class FruitsPage extends StatefulWidget {
  const FruitsPage({super.key});
  @override
  State<FruitsPage> createState() => _FruitsPageState();
}

class _FruitsPageState extends State<FruitsPage> {
  String _searchText = '';
  List _filteredShopItems = [];

  @override
  void initState() {
    super.initState();
    _filteredShopItems = cartmode.shopItems;
  }

  void _filterShopItems(String query) {
    setState(() {
      _searchText = query;
      _filteredShopItems = cartmode.shopItems
          .where((item) => item[0].toLowerCase().contains(query.toLowerCase()))
          .toList();
    });
  }

  CartModel cartmode = CartModel();
  @override
  @override
  Widget build(BuildContext context) {
    Device.screenType == ScreenType.tablet;
    return Container(
        margin: EdgeInsets.only(top: 20),
        width: 100.w,
        height: 100.5.h,
        child: Container(
            width: 100.w,
            height: 20.5.h,
            child: Consumer<CartModel>(builder: (context, value, child) {
              return Scaffold(
                appBar: AppBar(
                    title: CupertinoSearchTextField(
                      autocorrect: true,
                      onSubmitted: (value) {
                        _filterShopItems(value);
                      },
                    ),
                    backgroundColor: Colors.white,
                    elevation: 0,
                    iconTheme: IconThemeData(
                      color: Colors.grey[800],
                    )),
                body: GridView.builder(
                  padding: const EdgeInsets.all(12),
                  //physics: const NeverScrollableScrollPhysics(),
                  itemCount: value.shopItems.length,
                  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 2,
                    childAspectRatio: 1 / 1.2,
                  ),
                  itemBuilder: (context, index) {
                    return GroceryItemTile(
                      itemName: value.shopItems[index][0].toString(),
                      itemPrice: int.parse(
                          value.shopItems[index][1].toString()),
                      imagePath: (value.shopItems[index][2]),
                      color: value.shopItems[index][3],
                      onPressed: () =>
                          Provider.of<CartModel>(context, listen: false)
                              .addItemToCart(index),
                    );
                  },
                ),
              );
            })));
  }
}

I would be happy if someone can give me some advice, I don't understand why the search doesn't give me results, and it also prints an error in the console, namely 'The following _TypeError was thrown while calling onSubmitted for TextInputAction.search: type '(dynamic) => dynamic' is not a subtype of type '(dynamic) => bool' of 'test''

sams
  • 43
  • 6
  • Couple of advices to start from: 1- You compare only the query to the first item in the list: `where((item) => item[0].toLowerCase()...` should be `where((item) => item. toLowerCase()...` || 2- Don't avoid warnings by adding //ignore in your code when you are beginning. – Colin Lazarini Apr 20 '23 at 18:48
  • I tried your fix but nothing changes – sams Apr 20 '23 at 19:51
  • I think `item[0].toLowerCase()...` is not wrong since it's a double list List>. We want to lowercase `"Avocado"`, not `["Avocado", "4", "assets/images/8c.png", Colors.green]` – Yukinosuke Takada Apr 21 '23 at 06:59

1 Answers1

1

Your _filterShopItems() function works as expected. The problem is, you are mixing 2 state management for your code. Let me explain... I see in your code you are using StatefulWidget, and Provider.

So your code is running like this. When you submit a query from CupertinoSearchTextField(), the _filterShopItems() function runs, This will call setState() and _filteredShopItems will be altered accordingly to your query. Because you called setState(), FruitsPage rebuilds. Next, see itemBuilder inside GridView.builder(). It builds the shopping items with value. This value is CartModel from Provider which is your unfiltered shopping list (_filterShopItems will not change the state inside your CartModel Provider. Only _filteredShopItems which is inside your widget). As a result, your widget shows the unfiltered shopping list.

To solve this issue you have 2 choices. Either you manage state inside StatefulWiget or use Provider.

  1. Using StatefulWidget

    Inside itemBuilder change value.shopItems to _filteredShopItems. Example: value.shopItems[index][1] -> _filteredShopItems[index][1]

  2. Use Provider (Maybe difficult to understand)

    The logic inside _filterShopItems() should be changed. You should alter the
    CartModel.shopItems instead of _filteredShopItems. since Provider takes care of the state management you don't need to call setState(). In addition, _filteredShopItems is not used so you can remove some of the variables and FruitsPage does not need to be statefulWidget anymore...

Hope it helps! feel free to ask additional questions

edit:

I didn't see you had another problem

The following _TypeError was thrown while calling onSubmitted for TextInputAction.search: type '(dynamic) => dynamic' is not a subtype of type '(dynamic) => bool' of 'test'

To fix this, it is simple.

get shopItems => _shopItems;
// Change this to
List get shopItems => _shopItems;

this error occurs because the .where can only come after List type. the get method returns a dynamic if you don't specify.

edit2:

Sample Code:

class CartModel extends ChangeNotifier {
  final List _shopItems = const [
    // [ itemName, itemPrice, imagePath, color ]
    ["Avocado", "4", "assets/images/8c.png", Colors.green],
    ["Banana", "2", "assets/images/8c.png", Colors.yellow],
    ["pollo", "12", "assets/images/8c.png", Colors.brown],
    ["acqua", "1", "assets/images/8c.png", Colors.blue],
    ["mela", "3", "assets/images/8c.png", Colors.red],
    ["broccoli", "3", "assets/images/8c.png", Colors.brown],
  ];
  List get shopItems => _shopItems;
}

class UnnamedWidget extends StatefulWidget {
  const UnnamedWidget({super.key});

  @override
  State<UnnamedWidget> createState() => _UnnamedWidgetState();
}

class _UnnamedWidgetState extends State<UnnamedWidget> {
  List _filteredShopItems = [];
  String _searchText = "";
  CartModel cartmodel = CartModel();

  @override
  void initState() {
    super.initState();

    _filteredShopItems = cartmodel.shopItems;
  }

  void _filterShopItems(String query) {
    setState(() {
      _searchText = query;
      _filteredShopItems = cartmodel.shopItems
          .where((item) => item[0].toLowerCase().contains(query.toLowerCase()))
          .toList();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        children: [
          CupertinoSearchTextField(
            placeholder: "Search",
            onSubmitted: (String value) {
              _filterShopItems(value);
            },
          ),
          SizedBox(
            height: 400,
            child: GridView.builder(
              itemCount: _filteredShopItems.length,
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                childAspectRatio: 1 / 1.2,
                // childAspectRatio: 5,
              ),
              itemBuilder: (context, index) {
                return GroceryItemTile(
                  itemName: _filteredShopItems[index][0].toString(),
                  itemPrice: int.parse(
                      _filteredShopItems[index][1].toString()),
                  imagePath: (_filteredShopItems[index][2]),
                  color: _filteredShopItems[index][3],
                  onPressed: () =>
                      Provider.of<CartModel>(context, listen: false)
                          .addItemToCart(index),
                  // onPressed: () {},
                );
              }
            ),
          ),
        ],
      )
    );
  }
}

class GroceryItemTile extends StatelessWidget {
  final String itemName;
  final int itemPrice;
  final String imagePath;
  final Color color;
  final void Function()? onPressed;

  const GroceryItemTile({
    super.key,
    required this.itemName,
    required this.itemPrice,
    required this.imagePath,
    required this.color,
    required this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(itemName),
      trailing: Container(
        width: 10,
        height: 10,
        decoration: BoxDecoration(
          color: color,
          shape: BoxShape.circle,
        ),
      ),
    );
  }
}
  • I'm sorry for the late reply, could you provide me with an example of code so I can understand better? thank you very much – sams Apr 21 '23 at 14:40
  • I tried to modify the shopItems list a bit by putting List shopItems and modifying the return GroceryItemTile to see the FilteredShopItems but now when I click the button that is required on the GroceryItemTile onpress it doesn't work anymore – sams Apr 21 '23 at 14:43
  • If you want I can edit the question to make you understand – sams Apr 21 '23 at 14:44
  • sure I'll add my sample code later. I noticed you asked another question. I have edited my answer. – Yukinosuke Takada Apr 21 '23 at 18:45
  • yes, I just posted a new question on my profile to be able to better explain the problem and obviously see more solutions – sams Apr 21 '23 at 18:52
  • I've included my sample code – Yukinosuke Takada Apr 21 '23 at 18:55
  • thank you ,you can also look at the other question if you want – sams Apr 21 '23 at 18:57
  • I tried your fix and it works the items are filtered and added and there are no more errors in the cart but it reads each product as item[0] i.e. 'Avocado' – sams Apr 21 '23 at 19:13
  • do you mean that the grid view shows multiple avocado tiles? – Yukinosuke Takada Apr 21 '23 at 19:26
  • no everything works except when I look for an item and find it and add it to the cart it adds the product with index 0 i.e. Avocado I think the problem is in the item[0].lowercase but if I remove those brackets the search doesn't work anymore – sams Apr 21 '23 at 19:28
  • if I search for 'Banana' and add it to the cart it adds 'Avocado' while if I add the product manually it obviously adds the correct one – sams Apr 21 '23 at 19:31