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

时间:2018-08-22 21:53:41

标签: select button widget flutter dropdown

我正在扑扑地寻找一个等于的小部件

<select multiple=""></select>

扑朔迷离。

(对于网络)示例实现为MaterializeCSS Select Multiple

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

非常感谢示例实现或指向文档的链接。

3 个答案:

答案 0 :(得分:2)

我认为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);
}

答案 1 :(得分:0)

尝试此操作以支持动态值列表(如飞镖模型/集合)作为项,并且您还可以获取所选值中的文本值(如@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<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);
  });

  }
}

答案 2 :(得分:0)

这是你想要的吗?

Multiple Selection Dialog

如果您需要简短且随时可用的代码,请关注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(),
        ),
      ),
    );
  }
}