简体   繁体   English

Flutter 中是否有与 HTML 中的“select multiple”元素等效的小部件

[英]Is there an equivalent widget in flutter to the "select multiple" element in HTML

I am searching for a widget in flutter that is equal to我在颤振中寻找一个小部件,它等于

<select multiple=""></select>

in flutter.在颤抖。

An example implementation (for the web) is MaterializeCSS Select Multiple一个示例实现(用于网络)是MaterializeCSS Select Multiple

As seen above I should be able to provide a list of items (with some of them preselected) and at the end retrieve a list of selected items or a map or something else.如上所述,我应该能够提供一个项目列表(其中一些是预选的),最后检索所选项目的列表或地图或其他东西。

An example implementation or a link to a documentation is very appreciated.非常感谢示例实现或指向文档的链接。

I don't think that a widget like that currently exists in Flutter, but you can build one yourself.我认为 Flutter 中目前不存在这样的小部件,但您可以自己构建一个。

On mobile phones with limited screen space it would probably make sense to display a dialog with a submit button, like this native Android dialog .在屏幕空间有限的手机上,显示一个带有提交按钮的对话框可能是有意义的,比如这个原生 Android 对话框

Here is a rough sketch how to implement such a dialog in less than 100 lines of code:以下是如何用不到 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),
    );
  }
}

You can use it like this:你可以这样使用它:

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);
}

Try to this to support list of values as dynamic like (dart model/ collection) as a items and also you can get text value in selected value (As @urvashi commented in the above answer)尝试这样做以支持将值列表作为动态(例如(dart 模型/集合))作为项目,并且您还可以获得所选值中的文本值(正如@urvashi 在上述答案中评论的那样)

First of make the model class首先制作模型类

class BuildingModel {
  String id;
  String number;

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

After of make the class as above在使类如上之后

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);
  });

  }
}

Then finally you implement like this, ( don't forget to change showDialog data type It should be your item type showDialog<Set<BuildingModel>> )然后最后你这样实现,(不要忘记改变 showDialog 数据类型它应该是你的项目类型showDialog<Set<BuildingModel>>

 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);
  });

  }
}

Is this what you want?这是你想要的吗?

多选对话框

In case you need a short and ready to use code, follow this article如果你需要一个短期和准备使用的代码,请按照文章

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(),
        ),
      ),
    );
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM