How to resolve flutter dropdownButtonFormField dynamic selection checking for dropdownButton's value

旧时模样 提交于 2021-01-07 02:43:47

问题


I am trying to ask the user to select the type of item in the first dropdown and then select from its corresponding available colours in the second dropdown. However, when after I have selected a colour (i.e. white) and now want to switch to another item that does not have this colour, an error is thrown:

"There should be exactly one item with [DropdownButton]'s value: white. \nEither zero or 2 or more
[DropdownMenuItem]s were detected with the same value"

Please help, I have already tried to setState at various places to update the values but this error still occurs.

The following is my code snippet:

StreamBuilder<QuerySnapshot>(
    stream: mainItemsSnapshots,
    builder: (context, snapshot) {
    if (snapshot.hasError) return Text("Error");
    switch (snapshot.connectionState) {
        case ConnectionState.waiting:
        return Center(child: CircularProgressIndicator());
        default:
        {
            List<DropdownMenuItem> dropdownMenuItems =
                snapshot.data.documents
                    .map((DocumentSnapshot mainItem) {
            return new DropdownMenuItem<String>(
                value: mainItem.documentID,
                child: Text(mainItem.documentID),
            );
            }).toList();
            return DropdownButtonFormField<String>(
            items: dropdownMenuItems,
            onChanged: (String value) {
                if (value != tempMainItemType) {
                    setState(() {
                    tempMainItemType = value;
                    tempItemColorsList.clear();
                    tempItemColorsList = [];
                    tempMainItemColor = null;
                    });
                }
                
                if (tempItemColorsList.isEmpty && value != null) {
                tempItemColorsList = snapshot.data.documents
                    .where((element) => element.documentID == value)
                    .first
                    .data["colors"]
                    .keys
                    .map((color) => color.toString())
                    .toList()
                    .cast<String>();
                }
                setState((){});
            },
            onSaved: (String value) {
                _order.mainItemType = value;
            },
            value: tempMainItemType,
            );
        }
    }
    },
),

// Main color
if (tempItemColorsList?.isNotEmpty)
    Padding(
    padding: const EdgeInsets.only(top: spacingGeneral),
    child: textFieldLabel(context, "Main color"),
    ),
if (tempItemColorsList?.isNotEmpty)
    DropdownButtonFormField(
    items: tempItemColorsList.map((String color) {
        return new DropdownMenuItem<String>(
        value: color,
        child: Text(color),
        );
    }).toList(),
    onSaved: (String value) {
        _order.mainColor = value;
    },
    value: tempMainItemColor,
    onChanged: (String value) {
        setState(() {
        tempMainItemColor = value;
        });
    },
    ),

回答1:


This maybe too late, but you can create a Map<String, List<String>> where the keys are the items of the first dropdown list, and the value will be the items of second dropdown list.

Here, I created a state that stores the selected item of the first dropdown list. Then I used it to map the items of the second dropdown list.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SampleDD(),
    );
  }
}

class SampleDD extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: DoubleDropdown(
          items: <String, List<String>>{
            'dark colors': ['black', 'gray', 'brown'],
            'light colors': ['white', 'yellow', 'green'],
          },
          onChangedFirst: (val) => print('Selected first: $val'),
          onChangedSecond: (val) => print('Selected second: $val'),
        ),
      ),
    );
  }
}

class DoubleDropdown extends StatefulWidget {
  DoubleDropdown({
    @required this.items,
    @required this.onChangedFirst,
    @required this.onChangedSecond,
  });

  final Map<String, List<String>> items;
  final ValueChanged<String> onChangedFirst;
  final ValueChanged<String> onChangedSecond;

  @override
  _DoubleDropdownState createState() => _DoubleDropdownState();
}

class _DoubleDropdownState extends State<DoubleDropdown> {
  String selectedKey;

  @override
  void initState() {
    super.initState();
    selectedKey = widget.items.keys.first;
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        _buildFirstDropdown(),
        _buildSecondDowndown(),
      ],
    );
  }

  Widget _buildFirstDropdown() => DropdownButtonFormField<String>(
        items: widget.items.keys.map((e) {
          return DropdownMenuItem<String>(
            child: Text(e),
            value: e,
          );
        }).toList(),
        onChanged: (val) {
          setState(() => selectedKey = val);
          widget.onChangedFirst(val);
        },
        value: selectedKey,
      );

  Widget _buildSecondDowndown() => DropdownButtonFormField<String>(
        items: widget.items[selectedKey].map((e) {
          return DropdownMenuItem<String>(
            child: Text(e),
            value: e,
          );
        }).toList(),
        onChanged: widget.onChangedSecond,
        value: widget.items[selectedKey].first,
      );
}


来源:https://stackoverflow.com/questions/61572284/how-to-resolve-flutter-dropdownbuttonformfield-dynamic-selection-checking-for-dr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!