2

I am building an app for an e-commerce system which can post data to the server. There are multiple item categories which have different and customizable values. For example, laptop category can have processor, ram, storage size attributes. Some of this attribute are textboxes (eg. model) while others need to be dropdowns (eg. processor - core i7, i5, etc...). The number of attributes can not be fixed since it depends on the category (might be added or removed anytime)

I was trying to build a form which will show this attributes as a textbox or dropdown depending on the attribute type (AttributeType column). I was able to show both the textboxes and dropdowns (with their elements successfully. The problem I have is accessing dropdown values to create a post request to the server.

Here is the code

 FutureBuilder<FormListModel>(
                future: _formList,
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                    return ListView.builder(
                      primary: false,
                      scrollDirection: Axis.vertical,
                      shrinkWrap: true,
                      itemCount: snapshot.data.customattributes.length,
                      itemBuilder: (context, index) {
                        var item = snapshot.data.customattributes[index];
                        print(item.name);
                        
                        if (item.AttributeType == 'Text') {
                          return Container(
                            //padding: EdgeInsets.only(top: 8),
                            margin:
                                EdgeInsets.only(top: 15, left: 15, right: 15),
                            child: Column(
                              children: [
                                Form(
                                  child: TextFormField(
                                    controller: model_controller,
                                    decoration: InputDecoration(
                                      contentPadding: EdgeInsets.symmetric(
                                          vertical: 10, horizontal: 10),
                                      labelText: item.name,
                                      border: OutlineInputBorder(
                                        borderSide: BorderSide(
                                            color: Colors.blueAccent),
                                      ),
                                    ),
                                    onChanged: (value) {
                                      //print(model_controller.text);
                                    },
                                  ),
                                ),
                              ],
                            ),
                          );
                        } else if (item.AttributeType == 'Selectlist') {
                          return Container(
                            //padding: EdgeInsets.only(top: 8),
                            margin:
                                EdgeInsets.only(top: 20, left: 15, right: 15),
                            child: Column(
                              children: [
                                Form(
                                  child: InputDecorator(
                                    decoration: InputDecoration(
                                      contentPadding: EdgeInsets.symmetric(
                                          vertical: 12, horizontal: 12),
                                      labelText: item.name,
                                      labelStyle: TextStyle(
                                          fontSize: 20,
                                          color: Colors.blueAccent),
                                      border: const OutlineInputBorder(),
                                    ),
                                    child: DropdownButtonHideUnderline(
                                      child: DropdownButton(
                                        isDense: true,
                                        icon: Icon(Icons.keyboard_arrow_down),
                                        value: selectedAttribute,
                                        onChanged: (newValue) {
                                          setState(() {
                                            selectedAttribute = newValue;
                                          });
                                        },
                                        //decoration: InputDecoration(border: InputBorder.none),
                                        items: item.children
                                            .map<DropdownMenuItem>((items) {
                                          return DropdownMenuItem<String>(
                                            child: Row(
                                              children: [
                                                Padding(
                                                  padding:
                                                      EdgeInsets.only(top: 7),
                                                  child: Text(items.element),
                                                ),
                                              ],
                                            ),
                                            value: items.element,
                                          );
                                        }).toList(),
                                      ),
                                    ),
                                  ),
                                ),
                              ],
                            ),
                          );
                        } else {
                          return null;
                        }
                      },
                    );
                  } else {
                    return Container();
                  }
                }),

Here is the json file which we get from the server to create the form using ListView.builder

{
    "category": {
        "CategoryName": "Laptops",
        "CategoryID": 34
    },
    "customattributes": [
        {
            "Name": "Model",
            "AttributeType": "Text",
            "AttributeID": 7
        },
        {
            "Name": "Processor",
            "AttributeType": "Selectlist",
            "AttributeID": 2,
            "Children": [
                {
                    "Element": "Intel Core i3"
                },
                {
                    "Element": "Intel Core i5"
                },
                {
                    "Element": "Intel Core i7"
                }
            ]
        },
        {
            "Name": "Storage Size",
            "AttributeType": "Selectlist",
            "AttributeID": 1,
            "Children": [                
                {
                    "Element": "1TB"
                },
                {
                    "Element": "2TB"
                },
                {
                    "Element": "2.5TB"
                }
            ]
        },
        {
            "Name": "RAM",
            "AttributeType": "Selectlist",
            "AttributeID": 3,
            "Children": [
                {
                    "Element": "12GB"
                },
                {
                    "Element": "16GB"
            ]
        }
    ],
    
}

Here is the form

enter image description here

When I select any value from the dropdown, I get an error which says, There should be exactly one item with [DropdownButton]'s value

enter image description here

  1. So how can I access the values of each dropdown so that I can make http post request to the server. Please bear in mind that the number of dropdowns (attributes) is vary for each category. 2nd question, not that important but is there a way where I can assign a name for each dropdown using the 'Name' column of the attributes in the json file.
Abdi Nur
  • 490
  • 4
  • 9

4 Answers4

2

Try to set other dropdown value to null when changing the dropdown value in onChanged method

  • Is this to avoid seeing the error or to access the values? Because, I am pretty sure it won't make any sense to set other dropdown values to null if the users has already selected a value from the dropdown. – user3659497 Jan 27 '21 at 08:34
  • 1
    i am pretty sure making null will work because it worked for me in past –  Jan 27 '21 at 08:35
0

The problem is about the assigned value and the item list of the dropdown.

Please be mentioned that the value of a dropdown can only be a value from the items of the dropdown. And for custom values, you will have to compare the value of the selected item and put the index of the items as the current value. Hope you understand my concerns.

Follow the below code:

value : getSelectedValue(yourValue);
...
getSelectedValue(yourValue){
    //add this line of code
    if(yourValue == null) return null;
    for(value in dropDownItems){
        if(value.id == yourValue.id) return value;
    }
    return null;
}
Gourango Sutradhar
  • 1,461
  • 10
  • 16
0

I had the same problem too... I managed to solve it by checking whether my list is empty..if it is i do set the value to null else the first item on the list is selected

value: Provider.of<DepositProvider>(context)
                          .paymentMethods.isNotEmpty ?Provider.of<DepositProvider>(context)
                          .paymentMethods.first : null,
D. Ndungu
  • 131
  • 1
  • 8
0

You can solve it like this method:

    String? _selectedItem;

  @override
  void initState() {
    _selectedItem = widget.items?[0];
    super.initState();
  }

  void _changeSelectedPickList(String? pickListData) {
    setState(() {
      _selectedItem = pickListData;
    });
    widget.onChanged(pickListData);
  }

  getSelectedValue(String yourValue) {
    if (yourValue == null) return null;
    for (var value in widget.items!) {
      if (value == yourValue) return value;
    }
    return null;
  }

getSelectedValue func solved my problem full code here:

import 'package:flutter/material.dart';
import 'package:kartal/kartal.dart';

import '../../../feature/basket/viewmodel/cubit/basket_cubit.dart';
import '../profile/payment_dropdown_text_widget.dart';

class CurrencyListDropdown extends StatefulWidget {
  const CurrencyListDropdown(
      {Key? key,
      required this.items,
      this.selectedItems,
      required this.onChanged,
      required this.hint,
      this.basketCubit})
      : super(key: key);
  final List<String?>? items;
  final BasketCubit? basketCubit;

  final String? selectedItems;
  final String hint;

  final void Function(String? model) onChanged;
  @override
  State<CurrencyListDropdown> createState() => _CurrencyListDropdownState();
}

class _CurrencyListDropdownState extends State<CurrencyListDropdown> {
  String? _selectedItem;

  @override
  void initState() {
    _selectedItem = widget.items?[0];
    super.initState();
  }

  void _changeSelectedPickList(String? pickListData) {
    setState(() {
      _selectedItem = pickListData;
    });
    widget.onChanged(pickListData);
  }

  getSelectedValue(String yourValue) {
    if (yourValue == null) return null;
    for (var value in widget.items!) {
      if (value == yourValue) return value;
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return DropdownButtonFormField<String?>(
        value: getSelectedValue(_selectedItem!),
        isExpanded: true,
        isDense: false,
        decoration: InputDecoration(
          isCollapsed: true,
          contentPadding: EdgeInsets.zero,
          border:
              OutlineInputBorder(borderSide: BorderSide(color: context.colorScheme.onErrorContainer.withOpacity(0.7))),
          enabledBorder:
              OutlineInputBorder(borderSide: BorderSide(color: context.colorScheme.onErrorContainer.withOpacity(0.7))),
        ),
        hint: Center(
            child: Text(
          widget.hint,
          style: context.textTheme.titleSmall,
          textAlign: TextAlign.center,
        )),
        onChanged: _changeSelectedPickList,
        items: widget.items?.map((e) {
          return _dropdownItem(e, context);
        }).toList());
  }

  DropdownMenuItem<String> _dropdownItem(String? e, BuildContext context) {
    return DropdownMenuItem(
        value: e,
        child: PaymentDropdownTextWidget(
          label: e,
        ));
  }
}