Home > database >  How to filter a listview with buttons?
How to filter a listview with buttons?

Time:08-02

I'm trying to develop a restaurant menu app and i need that when i click one of the text buttons bellow it filters the items according to which button i tap. when i built the "meal" object, i created an atribute called "idDiaSem", and i have 5 diferent id's that i need to display 5 diferent lists of itens based on the ID i choose. Here's the concept:

Screenshot

Here's a complete minimal-reproducible-example:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      home: MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var _idDiaSem = 'seg';

  @override
  Widget build(BuildContext context) {
    final dayOfWeekMeals = meals
        .where((meal) => meal.idDiaSem.any((idDiaSem) => idDiaSem == _idDiaSem))
        .toList();

    return Scaffold(
      appBar: AppBar(
        title: const Text('Gastronomia'),
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: dayOfWeekMeals.length,
              itemBuilder: (ctx, index) {
                return Card(
                  elevation: 0,
                  margin: (index == dayOfWeekMeals.length - 1)
                      ? const EdgeInsets.only(bottom: 0, left: 20, right: 20)
                      : const EdgeInsets.only(left: 20, right: 20),
                  child: ClipRRect(
                    borderRadius: (index == 0)
                        ? const BorderRadius.only(
                            topLeft: Radius.circular(50),
                            topRight: Radius.circular(50),
                          )
                        : (index == dayOfWeekMeals.length - 1)
                            ? const BorderRadius.only(
                                bottomLeft: Radius.circular(50),
                                bottomRight: Radius.circular(50),
                              )
                            : BorderRadius.circular(0),
                    child: Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Text(dayOfWeekMeals[index].descricao),
                    ),
                  ),
                );
              },
            ),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              SemanaButton(
                'Segunda',
                onPressed: () => setState(() => _idDiaSem = 'seg'),
                selected: _idDiaSem == 'seg',
              ),
              SemanaButton(
                'Terça',
                onPressed: () => setState(() => _idDiaSem = 'ter'),
                selected: _idDiaSem == 'ter',
              ),
              SemanaButton(
                'Quarta',
                onPressed: () => setState(() => _idDiaSem = 'qua'),
                selected: _idDiaSem == 'qua',
              ),
              SemanaButton(
                'Quinta',
                onPressed: () => setState(() => _idDiaSem = 'qui'),
                selected: _idDiaSem == 'qui',
              ),
              SemanaButton(
                'Sexta',
                onPressed: () => setState(() => _idDiaSem = 'sex'),
                selected: _idDiaSem == 'sex',
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class SemanaButton extends StatelessWidget {
  final String text;
  final bool? selected;
  final VoidCallback onPressed;
  const SemanaButton(
    this.text, {
    Key? key,
    required this.onPressed,
    this.selected,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onPressed,
      child: Text(
        '$text \n____________',
        textAlign: TextAlign.center,
        style: TextStyle(
          fontSize: 11,
          color:
              selected == true ? Theme.of(context).colorScheme.primary : null,
          fontWeight: selected == true ? FontWeight.bold : null,
        ),
      ),
    );
  }
}

class Meal {
  final String id;
  final String descricao;
  final List<String> ingredients;
  final List<String> idDiaSem;
  final String imageUrl;

  const Meal({
    required this.id,
    required this.descricao,
    required this.ingredients,
    required this.idDiaSem,
    required this.imageUrl,
  });
}

var id = 0;

final meals = [
  Meal(
    id: '${  id}',
    descricao: 'Feijão Tropeiro',
    ingredients: [],
    idDiaSem: ['seg'],
    imageUrl: '',
  ),
  Meal(
    id: '${  id}',
    descricao: 'Feijoada',
    ingredients: [],
    idDiaSem: ['sex'],
    imageUrl: '',
  ),
  Meal(
    id: '${  id}',
    descricao: 'Batata Doce Caramelada',
    ingredients: [],
    idDiaSem: ['seg'],
    imageUrl: '',
  ),
  Meal(
    id: '${  id}',
    descricao: 'Cubos Suínos ao Molho Escuro',
    ingredients: [],
    idDiaSem: ['seg'],
    imageUrl: '',
  ),
  Meal(
    id: '${  id}',
    descricao: 'Enrolado de Salsicha',
    ingredients: [],
    idDiaSem: ['seg'],
    imageUrl: '',
  ),
  Meal(
    id: '${  id}',
    descricao: 'Doce e Fruta',
    ingredients: [],
    idDiaSem: ['seg', 'ter', 'qua', 'qui', 'sex'],
    imageUrl: '',
  ),
  Meal(
    id: '${  id}',
    descricao: 'Buffet de Saladas',
    ingredients: [],
    idDiaSem: ['seg', 'ter', 'qua', 'qui', 'sex'],
    imageUrl: '',
  ),
];

CodePudding user response:

Well, here is the recipe for the required logic:

  1. Define a variable to contain the id that you would to filter the meals according to.
int _filterId;
  1. When any filter button is pressed, just set the value of the _filterId inside a setState() block:
onTap: () {
    setState(() => _filterId = 0); // Take care of the ID
},
  1. At the begining of the build() method, Define a List of Meal that would contain the filtered meals in the future and set it to the dayOfWeekMeals list if the _filterId is null and apply the filter if it is not null, like the following:
List<Meal> _filteredMeals = _filterId == null ? dayOfWeekMeals : dayOfWeekMeals.where((meal) => meal.idDiaSem == _filterId).toList();
  1. Use the _filteredMeals list in the ListView instead of dayOfWeekMeals, for example:
itemCount: _filteredMeals.length,
MealItem(_filteredMeals[index])

NOTE: Don't forget to replace the others.


Side Note: You can start with an initial filter by just starting the variable _filterId with an initial id of the filter.

  • Related