是否有与 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<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);
);
【讨论】:
【参考方案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 中的“选择多个”元素等效的小部件的主要内容,如果未能解决你的问题,请参考以下文章
C++ 中是不是有与 python 中的 astype() 函数等效的函数?
是否有与 SQL Server NewId() 函数等效的 Access?