【问题标题】:Is there an equivalent widget in flutter to the "select multiple" element in HTML是否有与 HTML 中的“选择多个”元素等效的小部件
【发布时间】:2019-01-29 05:40:27
【问题描述】:

我在颤振中搜索一个等于

的小部件
<select multiple=""></select>

在颤抖中。

一个示例实现(用于网络)是MaterializeCSS Select Multiple

如上所示,我应该能够提供一个项目列表(其中一些是预先选择的),最后检索一个选定项目列表或地图或其他东西。

非常感谢您提供示例实现或文档链接。

【问题讨论】:

    标签: select button widget flutter dropdown


    【解决方案1】:

    我认为 Flutter 中目前不存在这样的小部件,但您可以自己构建一个。

    在屏幕空间有限的手机上,显示带有提交按钮的对话框可能更有意义,例如this native Android dialog

    这是一个粗略的草图,如何在不到 100 行代码中实现这样的对话框:

    class MultiSelectDialogItem<V> {
      const MultiSelectDialogItem(this.value, this.label);
    
      final V value;
      final String label;
    }
    
    class MultiSelectDialog<V> extends StatefulWidget {
      MultiSelectDialog({Key key, this.items, this.initialSelectedValues}) : super(key: key);
    
      final List<MultiSelectDialogItem<V>> items;
      final Set<V> initialSelectedValues;
    
      @override
      State<StatefulWidget> createState() => _MultiSelectDialogState<V>();
    }
    
    class _MultiSelectDialogState<V> extends State<MultiSelectDialog<V>> {
      final _selectedValues = Set<V>();
    
      void initState() {
        super.initState();
        if (widget.initialSelectedValues != null) {
          _selectedValues.addAll(widget.initialSelectedValues);
        }
      }
    
      void _onItemCheckedChange(V itemValue, bool checked) {
        setState(() {
          if (checked) {
            _selectedValues.add(itemValue);
          } else {
            _selectedValues.remove(itemValue);
          }
        });
      }
    
      void _onCancelTap() {
        Navigator.pop(context);
      }
    
      void _onSubmitTap() {
        Navigator.pop(context, _selectedValues);
      }
    
      @override
      Widget build(BuildContext context) {
        return AlertDialog(
          title: Text('Select animals'),
          contentPadding: EdgeInsets.only(top: 12.0),
          content: SingleChildScrollView(
            child: ListTileTheme(
              contentPadding: EdgeInsets.fromLTRB(14.0, 0.0, 24.0, 0.0),
              child: ListBody(
                children: widget.items.map(_buildItem).toList(),
              ),
            ),
          ),
          actions: <Widget>[
            FlatButton(
              child: Text('CANCEL'),
              onPressed: _onCancelTap,
            ),
            FlatButton(
              child: Text('OK'),
              onPressed: _onSubmitTap,
            )
          ],
        );
      }
    
      Widget _buildItem(MultiSelectDialogItem<V> item) {
        final checked = _selectedValues.contains(item.value);
        return CheckboxListTile(
          value: checked,
          title: Text(item.label),
          controlAffinity: ListTileControlAffinity.leading,
          onChanged: (checked) => _onItemCheckedChange(item.value, checked),
        );
      }
    }
    

    你可以这样使用它:

    void _showMultiSelect(BuildContext context) async {
      final items = <MultiSelectDialogItem<int>>[
        MultiSelectDialogItem(1, 'Dog'),
        MultiSelectDialogItem(2, 'Cat'),
        MultiSelectDialogItem(3, 'Mouse'),
      ];
    
      final selectedValues = await showDialog<Set<int>>(
        context: context,
        builder: (BuildContext context) {
          return MultiSelectDialog(
            items: items,
            initialSelectedValues: [1, 3].toSet(),
          );
        },
      );
    
      print(selectedValues);
    }
    

    【讨论】:

    • 很好的答案。喜欢这个。如果你的价值观列表是动态的呢?
    • 如何获取选定值中的文本值? @boformer
    • @urvashi 您可以在列表中使用索引“selectedValues”。
    • 这几乎解决了我遇到的一个问题,但是有没有办法通过传入要创建的项目数量以及项目 ID 和项目名称来创建 MultiSelectionDialogItem 列表,然后打印选择的值有某种分隔符,比如逗号?
    【解决方案2】:

    尝试这样做以支持动态值列表(例如(飞镖模型/集合)作为项目,并且您可以在选定值中获取文本值(正如@urvashi 在上述答案中评论的那样)

    首先制作模型类

    class BuildingModel {
      String id;
      String number;
    
      String toString() {
        return '$id $number';
      }
      BuildingModel(this.id, this.number);
    }
    

    在上面的类之后

    class MultiSelectDialogItem<V> {
      const MultiSelectDialogItem(this.value, this.label);
    
      final V value;
      final String label;
    }
    
    class MultiSelectDialog<V> extends StatefulWidget {
      MultiSelectDialog({Key key, this.items, this.initialSelectedValues})
          : super(key: key);
    
      final List<MultiSelectDialogItem<V>> items;
      final Set<V> initialSelectedValues;
    
      @override
      State<StatefulWidget> createState() => _MultiSelectDialogState<V>();
    }
    
    class _MultiSelectDialogState<V> extends State<MultiSelectDialog<V>> {
      final _selectedValues = Set<V>();
    
      void initState() {
        super.initState();
        if (widget.initialSelectedValues != null) {
          _selectedValues.addAll(widget.initialSelectedValues);
        }
      }
    
      void _onItemCheckedChange(V itemValue, bool checked) {
        setState(() {
          if (checked) {
            _selectedValues.add(itemValue);
          } else {
            _selectedValues.remove(itemValue);
          }
        });
      }
    
      void _onCancelTap() {
        Navigator.pop(context);
      }
    
      void _onSubmitTap() {
        Navigator.pop(context, _selectedValues);
      }
    
      @override
      Widget build(BuildContext context) {
        return AlertDialog(
          title: Text('Select wing'),
          contentPadding: EdgeInsets.only(top: 12.0),
          content: SingleChildScrollView(
            child: ListTileTheme(
              contentPadding: EdgeInsets.fromLTRB(14.0, 0.0, 24.0, 0.0),
              child: ListBody(
                children: widget.items.map(_buildItem).toList(),
              ),
            ),
          ),
          actions: <Widget>[
            FlatButton(
              child: Text('CANCEL'),
              onPressed: _onCancelTap,
            ),
            FlatButton(
              child: Text('SAVE'),
              onPressed: _onSubmitTap,
            )
          ],
        );
      }
    
      Widget _buildItem(MultiSelectDialogItem<V> item) {
        final checked = _selectedValues.contains(item.value);
        return CheckboxListTile(
          value: checked,
          title: Text(item.label),
          controlAffinity: ListTileControlAffinity.leading,
          onChanged: (checked) => _onItemCheckedChange(item.value, checked),
        );
      }
    }
    
      void _showMultiSelect(BuildContext context) async {
        final items = buildingDropdownItems;
    
        final selectedValues = await showDialog<Set<BuildingModel>>(
          context: context,
          builder: (BuildContext context) {
            return MultiSelectDialog(
              items: items,
            );
          },
        );
    
      selectedValues.forEach((element) {
        print(element.id);
      });
    
      }
    }
    

    最后你这样实现,(别忘了改变showDialog数据类型应该是你的项目类型showDialog&lt;Set&lt;BuildingModel&gt;&gt;

     void _showMultiSelect(BuildContext context) async {
        final items = buildingDropdownItems;
    
        final selectedValues = await showDialog<Set<BuildingModel>>(
          context: context,
          builder: (BuildContext context) {
            return MultiSelectDialog(
              items: items,
            );
          },
        );
    
      // here print your value or use per your need
      selectedValues.forEach((element) {
         print(element.id);
         print(element.number);
      });
    
      }
    }
    

    【讨论】:

      【解决方案3】:

      这是你想要的吗?

      如果您需要简短且可立即使用的代码,请关注this 文章

      import 'package:flutter/material.dart';
      import 'package:multiple_selection_dialogue_app/widgets/multi_select_dialog.dart';
      
      /// A demo page that displays an [ElevatedButton]
      class DemoPage extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          /// Stores the selected flavours
          List<String> flavours = [];
      
          return ElevatedButton(
              child: Text('Flavours'),
              onPressed: () async {
                flavours = await showDialog<List<String>>(
                        context: context,
                        builder: (_) => MultiSelectDialog(
                                question: Text('Select Your Flavours'),
                                answers: [
                                  'Chocolate',
                                  'Caramel',
                                  'Vanilla',
                                  'Peanut Butter'
                                ])) ??
                    [];
                print(flavours);
                // Logic to save selected flavours in the database
              });
        }
      }
      
      
      import 'package:flutter/material.dart';
      
      /// A Custom Dialog that displays a single question & list of answers.
      class MultiSelectDialog extends StatelessWidget {
        /// List to display the answer.
        final List<String> answers;
      
        /// Widget to display the question.
        final Widget question;
      
        /// List to hold the selected answer
        /// i.e. ['a'] or ['a','b'] or ['a','b','c'] etc.
        final List<String> selectedItems = [];
      
        /// Map that holds selected option with a boolean value
        /// i.e. { 'a' : false}.
        static Map<String, bool> mappedItem;
      
        MultiSelectDialog({this.answers, this.question});
      
        /// Function that converts the list answer to a map.
        Map<String, bool> initMap() {
          return mappedItem = Map.fromIterable(answers,
              key: (k) => k.toString(),
              value: (v) {
                if (v != true && v != false)
                  return false;
                else
                  return v as bool;
              });
        }
      
        @override
        Widget build(BuildContext context) {
          if (mappedItem == null) {
            initMap();
          }
          return SimpleDialog(
            title: question,
            children: [
              ...mappedItem.keys.map((String key) {
                return StatefulBuilder(
                  builder: (_, StateSetter setState) => CheckboxListTile(
                      title: Text(key), // Displays the option
                      value: mappedItem[key], // Displays checked or unchecked value
                      controlAffinity: ListTileControlAffinity.platform,
                      onChanged: (value) => setState(() => mappedItem[key] = value)),
                );
              }).toList(),
              Align(
                  alignment: Alignment.center,
                  child: ElevatedButton(
                      style: ButtonStyle(visualDensity: VisualDensity.comfortable),
                      child: Text('Submit'),
                      onPressed: () {
                        // Clear the list
                        selectedItems.clear();
      
                        // Traverse each map entry
                        mappedItem.forEach((key, value) {
                          if (value == true) {
                            selectedItems.add(key);
                          }
                        });
      
                        // Close the Dialog & return selectedItems
                        Navigator.pop(context, selectedItems);
                      }))
            ],
          );
        }
      }
      
      import 'package:flutter/material.dart';
      import 'package:multiple_selection_dialogue_app/pages/demo_page.dart';
      
      void main() {
        runApp(MyApp());
      }
      
      class MyApp extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            home: Scaffold(
              body: Center(
                child: DemoPage(),
              ),
            ),
          );
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-08-09
        • 1970-01-01
        • 2011-09-21
        • 2012-08-12
        • 1970-01-01
        • 2012-01-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多