我需要从其余Web服务动态加载列表城市,并让用户从警报对话框中选择一个城市。我的代码:
createDialog() {
fetchCities().then((response) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Wybierz miasto'),
content: Container(
height: 200.0,
width: 400.0,
child: ListView.builder(
shrinkWrap: true,
itemCount: response.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(response[index].name),
onTap: () => citySelected(response[index].id),
);
},
),
),
);
}
);
});
}
结果-对话框始终为200x400,即使只有2个城市可用,底部也没有多余的空间:
如何使对话框的宽度/高度适合实际项目的大小?如果我省略了height
和width
参数,则表示异常,并且未显示任何对话框。在本机Android Java中,我不需要指定任何尺寸,因为对话框本身会自动调整大小。
如何修复代码以正确设置对话框大小?请注意,我不知道项目数,它是动态的。
[编辑]
根据建议,我将内容包裹在列中:
createDialog() {
fetchCities().then((response) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Wybierz miasto'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: response.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(response[index].name),
onTap: () => citySelected(response[index].id),
);
},
),
)
]
),
);
}
);
});
}
结果-例外:
I / flutter(5917):渲染库引起的异常 ╞═════════════════════════════════════════════════ ════════我/颤振( 5917):在performLayout()期间引发了以下断言: I / flutter(5917):RenderViewport不支持返回内在函数 尺寸。 I / flutter(5917):计算固有尺寸 将需要实例化视口的每个子代, I / flutter(5917):打破了视口懒惰的观点。
更多要测试的通用代码:
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Select city'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: 2,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text("City"),
onTap: () => {},
);
},
),
)
]
),
);
}
);
答案 0 :(得分:7)
我有类似的问题。
我通过在AlertDialog中添加scrollable: true
来解决它
更新后的代码为:
createDialog() {
fetchCities().then((response) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
scrollable: true,
title: Text('Wybierz miasto'),
content: Container(
height: 200.0,
width: 400.0,
child: ListView.builder(
shrinkWrap: true,
itemCount: response.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(response[index].name),
onTap: () => citySelected(response[index].id),
);
},
),
),
);
}
);
});
}
答案 1 :(得分:6)
不要在您的 useEffect(() => {
const results = data?.drinks?.filter(({ strDrink }) =>
data.strDrink.toLowerCase().includes(searchTerm)
) ?? [];
setSearchResults(results);
}, [searchTerm, data]);
中设置 mainAxisSize.min
,否则如果内容长于视口,您可能会遇到溢出错误。要解决此问题,请使用其中一种方法。
Column
包裹在 Column
中:SingleChildScrollView
AlertDialog(
content: SingleChildScrollView(
child: Column(
children: [...],
),
),
)
中使用 shrinkWrap: true
:ListView
答案 2 :(得分:5)
我知道已经很晚了,但是您尝试过吗?
svg-sprite-loader
答案 3 :(得分:3)
您可以看看SimpleDialog是如何做到的。
<script>
function format ( d ) {
// `d` is the original data object for the row
return '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">'+
'<tr>'+
'<td>Full name:</td>'+
'<td>'+d.owner+'</td>'+
'</tr>'+
'<tr>'+
'<td>Extension number:</td>'+
'<td>'+d.extn+'</td>'+
'</tr>'+
'<tr>'+
'<td>Extra info:</td>'+
'<td>And any further details here (images etc)...</td>'+
'</tr>'+
'</table>';
}
$(document).ready(function() {
var dt = $('#tblVendor').DataTable( {
"responsive": true,
"dom": 'Bfrtip',
"buttons":[
{ extend: 'copy', text: 'Copy to Clipboard', className: 'btn btn-info btn-fill' },
{ extend: 'pdf', text: 'Export PDF', className: 'btn btn-info btn-fill' },
{ extend: 'excel', text: 'Export Excel', className: 'btn btn-info btn-fill' },
{ extend: 'csv', text: 'Export CSV', className: 'btn btn-info btn-fill' },
{"className": "btn btn-info btn-fill move", "text":"New Vendor", "action":function(e, dt, node, config){window.location.replace("new.php"); }
}],
"processing": true,
"serverSide": true,
"ajax": "ajax.php",
'serverMethod': 'post',
"columns": [
{
"width": "5%",
"class": "details-control",
"orderable": false,
"data": null,
"defaultContent": ""
},
{ "data": "name" },
{ "data": "company" },
{ "data": "type" },
{ "data": "status" },
{ "data": "owner", "visible": false},
{ "data": "dept" },
], "order": [[1, 'asc']],
} );
new $.fn.dataTable.Buttons( table, {
buttons: [
{
text: 'Button 2',
action: function ( e, dt, node, conf ) {
alert( 'Button 2 clicked on' );
}
},
{
text: 'Button 3',
action: function ( e, dt, node, conf ) {
alert( 'Button 3 clicked on' );
}
}
]
} );
// Array to track the ids of the details displayed rows
var detailRows = [];
$('#tblVendor tbody').on( 'click', 'tr td.details-control', function () {
var tr = $(this).closest('tr');
var row = dt.row( tr );
var idx = $.inArray( tr.attr('id'), detailRows );
if ( row.child.isShown() ) {
tr.removeClass( 'details' );
row.child.hide();
// Remove from the 'open' array
detailRows.splice( idx, 1 );
}
else {
tr.addClass( 'details' );
row.child( format( row.data() ) ).show();
// Add to the 'open' array
if ( idx === -1 ) {
detailRows.push( tr.attr('id') );
}
}
} );
// On each draw, loop over the `detailRows` array and show any child rows
dt.on( 'draw', function () {
$.each( detailRows, function ( i, id ) {
$('#tblVendor'+id+' td.details-control').trigger( 'click' );
} );
} );
table.buttons( 1, null ).container().appendTo(
table.table().container()
);
} );
</script>
答案 4 :(得分:1)
将Container
包裹在Column
内的内容参数器中,在其内部的Column属性中设置mainAxisSize.min
答案 5 :(得分:1)
我遇到了一个非常相似的问题,并且想出了一个适用于Material和Cupertino的解决方案。
与带有可滚动标志= true和带有mainAxisSize:MainAxisSize.min的Column的警报对话框相比,性能(尤其是元素列表变长)的方式是更好地加载和滚动内容的方式-只需在此处观看视频:https://www.youtube.com/watch?v=2nKTGFZosr0
此外,对话框的标题不会与其他元素一起“向上滚动”(类似于您的解决方案),因此您可以在顶部添加“说”过滤工具,仅显示与搜索短语匹配的元素
可在此处找到源代码https://github.com/hicnar/fluttery_stuff只需检查整个内容并运行lib / dialogs / main.dart中的main()显然,您可以按照自己喜欢的任何方式复制,粘贴,修改和使用它。这里没有版权。
最后,在示例中,我将基于ListView的对话框内容的高度限制为最大屏幕高度的45%,您会发现它很容易,如果将系数更改为1.0,您将获得与通过基于列的方法(搜索名为screenHeightFactor的字段)
答案 6 :(得分:0)
您可以尝试一下吗?
至少对我有用。
如果您需要一个例子,请告诉我。
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class SmartDialog extends StatelessWidget {
const SmartDialog({
Key key,
this.title,
this.titlePadding,
this.content,
this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
this.actions,
this.semanticLabel,
}) : assert(contentPadding != null),
super(key: key);
final Widget title;
final EdgeInsetsGeometry titlePadding;
final Widget content;
final EdgeInsetsGeometry contentPadding;
final List<Widget> actions;
final String semanticLabel;
@override
Widget build(BuildContext context) {
final List<Widget> children = <Widget>[];
String label = semanticLabel;
if (title != null) {
children.add(new Padding(
padding: titlePadding ?? new EdgeInsets.fromLTRB(24.0, 24.0, 24.0, content == null ? 20.0 : 0.0),
child: new DefaultTextStyle(
style: Theme.of(context).textTheme.title,
child: new Semantics(child: title, namesRoute: true),
),
));
} else {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
label = semanticLabel;
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
label = semanticLabel ?? MaterialLocalizations.of(context)?.alertDialogLabel;
}
}
if (content != null) {
children.add(new Flexible(
child: new Padding(
padding: contentPadding,
child: new DefaultTextStyle(
style: Theme.of(context).textTheme.subhead,
child: content,
),
),
));
}
if (actions != null) {
children.add(new ButtonTheme.bar(
child: new ButtonBar(
children: actions,
),
));
}
Widget dialogChild = new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children,
);
if (label != null)
dialogChild = new Semantics(
namesRoute: true,
label: label,
child: dialogChild
);
return new Dialog(child: dialogChild);
}
}
更新
您只需要在按钮或按下某些按钮后显示此AreaPicker。
class AreaPicker extends StatelessWidget {
final List<Area> items;
AreaPicker(this.items);
@override
Widget build(BuildContext context) {
return SmartDialog(
title: Text('Select Area'),
actions: <Widget>[
FlatButton(
textColor: Colors.black,
child: Text('Rather not say'),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
)
],
content: Container(
height: MediaQuery.of(context).size.height / 4,
child: ListView.builder(
shrinkWrap: true,
itemExtent: 70.0,
itemCount: areas.length,
itemBuilder: (BuildContext context, int index) {
final Area area = areas[index];
return GestureDetector(
child: Center(
child: Text(area.name),
),
onTap: () {
Navigator.of(context, rootNavigator: true).pop();
// some callback here.
}
);
},
),
)
);
}
}
答案 7 :(得分:0)
这是我的最终解决方案:
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
typedef Widget ItemBuilder<T>(T item);
class CityChoiceDialog<T> extends StatefulWidget {
final T initialValue;
final List<T> items;
final ValueChanged<T> onSelected;
final ValueChanged<T> onSubmitted;
final ValueChanged<T> onCancelled;
final Widget title;
final EdgeInsetsGeometry titlePadding;
final EdgeInsetsGeometry contentPadding;
final String semanticLabel;
final ItemBuilder<T> itemBuilder;
final List<Widget> actions;
final Color activeColor;
final String cancelActionButtonLabel;
final String submitActionButtonLabel;
final Color actionButtonLabelColor;
final Widget divider;
CityChoiceDialog({
Key key,
this.initialValue,
@required this.items,
this.onSelected,
this.onSubmitted,
this.onCancelled,
this.title,
this.titlePadding,
this.contentPadding = const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
this.semanticLabel,
this.actions,
this.itemBuilder,
this.activeColor,
this.cancelActionButtonLabel,
this.submitActionButtonLabel,
this.actionButtonLabelColor,
this.divider = const Divider(height: 0.0),
}) : assert(items != null),
super(key: key);
@override
_CityChoiceDialogState<T> createState() =>
_CityChoiceDialogState<T>();
}
class _CityChoiceDialogState<T>
extends State<CityChoiceDialog<T>> {
T _chosenItem;
@override
void initState() {
_chosenItem = widget.initialValue;
super.initState();
}
@override
Widget build(BuildContext context) {
return MyAlertDialog(
title: widget.title,
titlePadding: widget.titlePadding,
contentPadding: widget.contentPadding,
semanticLabel: widget.semanticLabel,
content: _buildContent(),
actions: _buildActions(),
divider: widget.divider,
);
}
_buildContent() {
return ListView(
shrinkWrap: true,
children: widget.items
.map(
(item) => RadioListTile(
title: widget.itemBuilder != null
? widget.itemBuilder(item)
: Text(item.toString()),
activeColor:
widget.activeColor ?? Theme.of(context).accentColor,
value: item,
groupValue: _chosenItem,
onChanged: (value) {
if (widget.onSelected != null) widget.onSelected(value);
setState(() {
_chosenItem = value;
});
},
),
)
.toList(),
);
}
_buildActions() {
return widget.actions ??
<Widget>[
FlatButton(
textColor:
widget.actionButtonLabelColor ?? Theme.of(context).accentColor,
child: Text(widget.cancelActionButtonLabel ?? 'ANULUJ'),
onPressed: () {
Navigator.pop(context);
if (widget.onCancelled!= null) widget.onCancelled(_chosenItem);
},
),
FlatButton(
textColor:
widget.actionButtonLabelColor ?? Theme.of(context).accentColor,
child: Text(widget.submitActionButtonLabel ?? 'WYBIERZ'),
onPressed: () {
Navigator.pop(context);
if (widget.onSubmitted != null) widget.onSubmitted(_chosenItem);
},
)
];
}
}
class MyAlertDialog<T> extends StatelessWidget {
const MyAlertDialog({
Key key,
this.title,
this.titlePadding,
this.content,
this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
this.actions,
this.semanticLabel,
this.divider = const Divider(
height: 0.0,
),
this.isDividerEnabled = true,
}) : assert(contentPadding != null),
super(key: key);
final Widget title;
final EdgeInsetsGeometry titlePadding;
final Widget content;
final EdgeInsetsGeometry contentPadding;
final List<Widget> actions;
final String semanticLabel;
final Widget divider;
final bool isDividerEnabled;
@override
Widget build(BuildContext context) {
final List<Widget> children = <Widget>[];
String label = semanticLabel;
if (title != null) {
children.add(new Padding(
padding: titlePadding ??
new EdgeInsets.fromLTRB(
24.0, 24.0, 24.0, isDividerEnabled ? 20.0 : 0.0),
child: new DefaultTextStyle(
style: Theme.of(context).textTheme.title,
child: new Semantics(child: title, namesRoute: true),
),
));
if (isDividerEnabled) children.add(divider);
} else {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
label = semanticLabel;
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
label = semanticLabel ??
MaterialLocalizations.of(context)?.alertDialogLabel;
}
}
if (content != null) {
children.add(new Flexible(
child: new Padding(
padding: contentPadding,
child: new DefaultTextStyle(
style: Theme.of(context).textTheme.subhead,
child: content,
),
),
));
}
if (actions != null) {
if (isDividerEnabled) children.add(divider);
children.add(new ButtonTheme.bar(
child: new ButtonBar(
children: actions,
),
));
}
Widget dialogChild = new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children,
);
if (label != null)
dialogChild =
new Semantics(namesRoute: true, label: label, child: dialogChild);
return new Dialog(child: dialogChild);
}
}
它基于https://pub.dev/packages/easy_dialogs,到目前为止,它可以正常工作。我正在分享它,因为它可能有用,但问题并非微不足道。