是否有与 HTML 中的“选择多个”元素等效的小部件

Posted

技术标签:

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

我在颤振中搜索一个等于

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

在颤抖中。

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

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

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

【问题讨论】:

【参考方案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(),
        ),
      ),
    );
  

【讨论】:

以上是关于是否有与 HTML 中的“选择多个”元素等效的小部件的主要内容,如果未能解决你的问题,请参考以下文章

css中是不是有与背景图像等效的前景?

C++ 中是不是有与 python 中的 astype() 函数等效的函数?

Blazor 中是不是有与 Html.Raw 等效的内容?

是否有与 SQL Server NewId() 函数等效的 Access?

等效于 Riverpod 中的 ChangeNotifierProvider 小部件

是否有与 Java 的 CountDownLatch 等效的 C#?