0

I am new on flutter and I try to create a dependent dropdown. I have 2 dropdowns fields and my data are the categories and the products. The first dropdown which display the categories is working. My problem is on the second dropdown, are not showing the options based on the selected value of categories. Below I add my data in a specific file.

localData.dart

import 'package:flutter/material.dart';

class LocalData with ChangeNotifier {
  static const List<Map<String, dynamic>> _categories = [
    {'id': 1, 'name': 'Electronics'},
    {'id': 2, 'name': 'Gaming'},
    {'id': 3, 'name': 'Accessories'},
  ];

  static List<Map<String, dynamic>> get categories {
    return [..._categories];
  }

  static List<Map<String, dynamic>> _products = [
    {
      'id': 1,
      'name': 'Smartphone',
      'categoriesId': [1]
    },
    {
      'id': 2,
      'name': 'Laptop',
      'categoriesId': [2, 3]
    },
    {
      'id': 3,
      'name': 'iPods',
      'categoriesId': [1, 3]
    },
  ];

  static List<Map<String, dynamic>> get products{
    return [..._products];
  }

Basically on the products data(_products) I have categoriesId which the list of ids are combine with the ids of categories(_categories). In my code I use the "where" method but is not working. Also I used the contains but I had the same result. This is my code:

home.dart

class _HomePageState extends ConsumerState<HomePage> {
  int? category;
  int? product;

  List<Map<String, dynamic>> loadProd = [];

  @override
  Widget build(BuildContext context) {
    final themeService = ref.watch(themeServiceProvider);

    final ancScaffold = Scaffold.maybeOf(context);

    getProductsOptions() {
      loadProd = LocalData.products
          .where(
            (prod) => prod['categoriesId'] == category,
          )
          .toList();

      if (LocalData.products.isNotEmpty) {
        return loadProd.map((val) {
          return DropdownMenuItem(
              value: val['id'], child: Container(child: Text(val['name'])));
        }).toList();
      }
    }

    return Scaffold(
      appBar: AppBar(        
        bottom: themeService.appbarBottom(),
        elevation: 0,
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            Material(
              child: Container(
                child: Form(
                  child: Column(
                    children: [
                      SizedBox(
                        child: DropdownButtonFormField(
                          value: category,
                          isExpanded: true,
                          decoration: InputDecoration(
                            labelText: 'Categories',
                          ),
                          items: LocalData.categories.map((cat) {
                            return DropdownMenuItem(
                                value: cat['id'],
                                child: Container(child: Text(cat['name'])));
                          }).toList(),
                          onChanged: (value) {
                            setState(() {
                              if (value == null) {
                                return;
                              }
                              category = value as int;
                              //print(categories);
                            });
                          },
                        ),
                      ),
                      SizedBox(
                        child: DropdownButtonFormField(
                          value: product,
                          isExpanded: true,
                          decoration: InputDecoration(
                            labelText: 'Products',
                          ),
                          //value: categories,
                          items: getProductsOptions(),
                          onChanged: (prod) {
                            setState(() {
                              if (prod == null) {
                                return;
                              }
                              product = prod as int;
                            });
                          },
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Any ideas?

Epi94
  • 31
  • 3

1 Answers1

0

You can use this method to get filter items,

  List<Map<String, dynamic>> getProductsByCategoryId(int id) {
    return _products.where((product) { // i would prefer model class+ try-catch
      return (product['categoriesId'] as List).contains(id);
    }).toList();
  }

Here is the test snippet

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

  @override
  State<DDW> createState() => _DDWState();
}

class _DDWState extends State<DDW> {
  final _categories = [
    {'id': 1, 'name': 'Electronics'},
    {'id': 2, 'name': 'Gaming'},
    {'id': 3, 'name': 'Accessories'},
  ];

  final List<Map<String, dynamic>> _products = [
    {
      'id': 1,
      'name': 'Smartphone',
      'categoriesId': [1]
    },
    {
      'id': 2,
      'name': 'Laptop',
      'categoriesId': [2, 3]
    },
    {
      'id': 3,
      'name': 'iPods',
      'categoriesId': [1, 3]
    },
  ];

  List<Map<String, dynamic>> getProductsByCategoryId(int id) {
    return _products.where((product) {
      return (product['categoriesId'] as List).contains(id);
    }).toList();
  }

  int? category;
  int? product;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          DropdownButton(
            items: _categories
                .map((e) => DropdownMenuItem<int?>(
                      child: Text(e['name'] as String),
                      value: int.tryParse(e['id'].toString()),
                    ))
                .toList(),
            onChanged: (value) {
              category = value as int?;
              product = null;
              setState(() {});
            },
            value: category,
          ),
          DropdownButton<int?>(
            items: getProductsByCategoryId(category ?? 0)
                .map(
                  (e) => DropdownMenuItem<int?>(
                    value: int.tryParse(e['id'].toString()),
                    child: Text(e['name'].toString()),
                  ),
                )
                .toList(),
            onChanged: (value) {
              product = value;
              setState(() {});
            },
            value: product,
          ),
        ],
      ),
    );
  }
}

You can check another example of multi-level dropdown

Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56