I'm a new flutter developer and would like to use multiple filters on my List. I'm using a ListView :


ListView.builder(
itemBuilder: (ctx, index) =>
MealCard(_customList[index]),
itemCount: _customList.length,
                          ),

and my Meal class have the following properties:

 Meal {
final String id,
final String title, 
final String imgUrl, 
final bool isLowCalorie,
final bool isVegan,
final bool isBreakfat, 
final bool isDinner,
final bool isLunch}

I'd like to apply different filters based on the booleans included within my class; I have some methods that look something like this:

 List<Meal> get breakfastFilter {
    return _meals.where((element) => element.isBreakfast).toList();
  }

  List<Meal> get lunchFilter {
    return _meals.where((element) => element.isLunch).toList();
  }

  List<Meal> get dinnerFilter {
    return _meals.where((element) => element.isDinner).toList();

However these methods allow me to create a single list, what if I wish to add additional filters together and create a combination of two lists.


Solution 1: Amir Mohammad Shams

You can do it something like this:

 bool filterItem(element){
    return element.isVegan&&element.isBreakfat;
  }

  _meals.where((element) => filterItem(element)).toList();  


Solution 2: ManuH68

There's many ways to do it but you can try to use boooleans combined with optional parameters like this:

getMealFilter(_meals,
    {bool lookForBreakfast = false,
    bool lookForVegan = false,
    bool lookForLunch = false}) {
  return _meals
      .where((element) =>
          (lookForBreakfast && element.isBreakfast) ||
          (lookForVegan && element.isBreakfast) ||
          (lookForLunch && element.isLunch))
      .toList();
}

I just put 3 filters, but you can easily add the rest. How this works is that if, for example, lookForBreakfast is false, element.isBreakfast is ignored. (false and something is always false), and so on for the others parameters.


Solution 3: Roman Jaquez

I know I'm late to the game, so let me throw my name in the hat. I'd refactor it a bit so as to support more than one filter, otherwise you won't know upfront how many filters will be applied.

I'd create an enum called MealType:

enum MealType {
  none,
  isLowCalorie,
  isVegan,
  isBreakfast,
  isDinner,
  isLunch
}

Refactor the class to take a list of MealType values:

class Meal {
  final String? id;
  final String? title;
  final String? imgUrl;
  final List<MealType>? mealTypeFilter;

  Meal({ this.id, this.title, this.imgUrl, this.mealTypeFilter });  
}

The data would look like this:

List<Meal> meals = [
    Meal(
      id: '1001',
      title: 'Pancakes',
      imgUrl: '',
      mealTypeFilter: [
        MealType.isBreakfast
      ]
    ),
    Meal(
      id: '1002',
      title: 'Chicken Wings',
      imgUrl: '',
      mealTypeFilter: [
        MealType.isLunch
      ]
    ),
    Meal(
      id: '1003',
      title: 'Yogurt',
      imgUrl: '',
      mealTypeFilter: [
        MealType.isLowCalorie,
        MealType.isBreakfast,
        MealType.isVegan
      ]
    )
  ];

I'd collect the applied filters in a Set to avoid duplicates, and filter the meals whether the mealTypeFilter collection contains any of the applied filters:


Set<MealType> appliedFilters = {};
  List<Meal> filteredList = [];

  List<Meal> getFilteredList() {
    if (appliedFilters.isEmpty) {
      return meals;
    }

    return meals.where((m) => m.mealTypeFilter!.any((f) => appliedFilters.contains(f))).toList();
  }

I'd call the getFilteredList() method every time in the build method when I trigger a button that contains the filter to be applied and populate the filteredList with which I render a ListView with the results:

@override
Widget build(BuildContext context) {

    filteredList = getFilteredList();

    // rest of the code here
}

See Gist for the full code, and run it through DartPad so you can see it in action, like this:

enter image description here

Hope this works.