Flutter - DropdownButton溢出问题

81

我正在尝试使用Flutter进行实验,目前我正在尝试在对话框中的列表视图中显示输入字段和下拉菜单。然而,我发现下拉菜单超出了视图的水平宽度,并导致黄灰色条纹图案(如下所示)

enter image description here 下拉菜单小部件在ListView中溢出

代码如下:

    class DataInput extends StatefulWidget {

      @override
      State createState() => new DataInputState("");
    }


    enum DismissDialogAction {
      cancel,
      discard,
      save,
    }

    class DataInputState extends State<DataInput> {
      final String _data;
      static const types = const <Map<String, String>>[
        const {
          "id": "103",
          "desc": "0001 - lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum"
        },
        const {
          "id": "804",
          "desc": "0002 - lorem ipsum lorem ipsum"
        },
      ];

      DataInputState(this._data);

      @override
      Widget build(BuildContext context) {
        final ThemeData theme = Theme.of(context);
        return new Scaffold(
          appBar: new AppBar(
            title: const Text("Details"),
            actions: <Widget>[
              new FlatButton(
                  onPressed: () => Navigator.pop(context, DismissDialogAction.save),
                  child: new Row(
                    children: <Widget>[
                      new Icon(Icons.save, color: Colors.white,),
                      new Text(
                          "Save",
                          style: theme.textTheme.body1.copyWith(
                            color: Colors.white,)
                      )
                    ],
                  )
              ),
            ],
          ),
          body: new ListView(
            shrinkWrap: true,
            children: <Widget>[
              new Text("Set Label"),
              new TextField(
                autocorrect: false,
              ),
              new Text("Select Type"),
              new Container(
                width: new FractionColumnWidth(0.5).value,
                child: new DropdownButton(
                    items: types.map((m) =>
                    new DropdownMenuItem(
                        key: new Key(m["id"]),
                        child: new Text(m["desc"]))
                    ).toList(growable: false),
                    onChanged: null
                ),
              ),
            ],
          ),
        );
      }
    }

错误:

    ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
    The following message was thrown during layout:
    A horizontal RenderFlex overflowed by 433 pixels.

    The edge of the RenderFlex that is overflowing has been marked in the rendering with a yellow and
    black striped pattern. This is usually caused by the contents being too big for the RenderFlex.
    RenderFlex to fit within the available space instead of being sized to their natural size.
    This is considered an error condition because it indicates that there is content that cannot be
    seen. If the content is legitimately bigger than the available space, consider clipping it with a
    RectClip widget before putting it in the flex, or using a scrollable container rather than a Flex,
    for example using ListView.
    The specific RenderFlex in question is:
    RenderFlex#cc264 relayoutBoundary=up12 OVERFLOWING
    creator: Row ← SizedBox ← DefaultTextStyle ← Stack ← Listener ← _GestureSemantics ←
    RawGestureDetector ← GestureDetector ← DropdownButton ← ConstrainedBox ← Container ←
    RepaintBoundary-[<3>] ← ⋯
    direction: horizontal
    mainAxisAlignment: space-between
    mainAxisSize: min
    crossAxisAlignment: center
    textDirection: ltr
    verticalDirection: down

我尝试了以下几种方法,但它们都不起作用:

  • 将下拉菜单包裹在“行”、“列”、“填充”和“剪辑矩形”中

有人可以帮助我理解这个问题,并展示如何修复吗?

更新 使用FittedBox可以防止溢出,但是文本大小会变得难以辨认。


6
尝试使用DropDownButton的isExpanded属性。来源:https://dev59.com/OFQJ5IYBdhLWcg3wu4ex#53213558 - Touré Holder
9个回答

214
最简单的解决方案是在DropdownButton中将isExpanded属性添加为true
例如:
new DropdownButton(
                  isExpanded: true, //Adding this property, does the magic
                  items: [
                    new DropdownMenuItem(
                        child: Text("Some large text that needs to be wrapped or ellipsized", 
                        overflow: TextOverflow.ellipsis),
                    ),
                    new DropdownMenuItem(
                      child: Text("This is another large text that needs to be wrapped or ellipsized", 
                      overflow: TextOverflow.ellipsis),
                    ),
                    new DropdownMenuItem(
                      child: Text("And one more large text that needs to be wrapped or ellipsized", 
                      overflow: TextOverflow.ellipsis),
                    )
                  ],
                  onChanged: (val) {
                    //print(val);
                  }),

5
如果您不希望所选的项目被省略,您还可以将此答案与DropdownButton中的selectedItemBuilder字段配对使用。或者,您可以指定itemHeight为null,这样它就可以比kMinInteractiveDimension更大。 - ckern
4
传说,我敢说 isExpanded: true 应该是默认值。 - velval
2
在设置此参数时,您可能希望将 DropdownButton 包装在一个 Expanded Widget 中。 - Linus H.
1
isExpanded: true, 这行代码真的很神奇,是个好的解决方案!谢谢。 - Vishva Vijay
1
阿拉丁神灯。 - Istiaque Ahmed

32

我通过将DropdownMenuItem的子项放在SizedBox中并给定SizedBox一个固定的宽度来解决了这个问题,这样可以避免UI溢出并使其看起来仍然很好。

例如:

                   new DropdownMenuItem<String>(
                      value: value,
                      child:  new SizedBox(
                        width: 200.0,
                        child: new Text('Long text with ${value} ')
                      ),
                    )

2
这个很好用。如果你想设置最小和最大宽度而不是固定宽度,你也可以使用ConstrainedBox而不是SizedBox。 - Daryl
@Daryl 是的,你可以使用ConstrainedBox或容器(并定义约束)。 - Raj Yadav
如果文本超过宽度维度,大小框将导致文本换行。而isexpanded会根据文本长度调整下拉项的宽度。 - Golden Lion
当SizedBox与一个扩展小部件在同一行时,它对我来说不起作用。但是FittedBox对我很有效。 - Texv
在Flutter的下拉菜单中启用,请点击向下箭头。 - s.j

29

尝试使用 isExpanded: true,

它会自动将文本扩展到新行

DropdownButton(
     isExpanded: true, //Add this property
     items: [
          DropdownMenuItem(
              child: Text("Your Text Here", 
              overflow: TextOverflow.ellipsis),
          ),
}),

请参考下面的图片

这一行没有足够的空间来放置文字,因此继续在下一行

图片描述


1
@iDecode,抱歉我不明白你为什么要给我的答案投反对票。我只是提供了一个对我有效的解决方案,并且与这个问题有关。如果你不喜欢这个解决方案,可以选择忽略它,对吧? - Vicky Salunkhe
4
我给出了反对票,因为这个答案已经提供了,用不同的措辞写相同的回答没有意义。希望你能理解。 - iDecode
我以为 "isExpanded" 会自动显示下拉列表。所以我不确定这是如何或为什么起作用的,但它确实起作用。 - GraSim

16

我认为您遇到了DropDownButton本身的一个合法错误。这里有一个关于此问题的Github问题:https://github.com/flutter/flutter/issues/9211

如果您需要立即修复,您实际上可以自己修补DropDownButton!操作步骤如下:

  1. 从Flutter框架中打开dropdown.dart并将其粘贴到您自己的项目中作为fixed_dropdown.dart
  2. 从该文件中删除DropDownMenuItem类,以避免与您正常的Flutter导入发生冲突。
  3. DropDownButton重命名为FixedDropDownButton,以避免与Flutter导入冲突。
  4. 导航到_DropdownButtonStatebuild方法。在Row内找到一个IndexedStack。使用一个Expanded小部件包装IndexedStack

我在Github问题本身上发布了这些信息,并且如果您想看到修复情况的截图,也可以在那里找到。


这个解决方案如果包装在Row小部件中会出现问题,因为Expanded会导致宽度无限增加。我在我的代码中修复了这个问题,通过将其包装在Stack小部件中并在TextView的右侧提供一些填充,并将箭头向下按钮放置在右侧。这对我来说很有效:https://github.com/apgapg/Flutter_Pull_Request/blob/master/fix_drop_down.dart - Ayush P Gupta

13

以上任何方法都未能正确解决此问题。

请尝试使用FittedBox,具体文档可以在这里查看。

只需像这样包装下拉菜单项的子项即可。

DropdownMenuItem<MyDropdownContentClass>(
            child: FittedBox(
              child: Text("dropdown item display"),
              fit: BoxFit.contain,
            ),
          );

在绘制时,它将自动调整整个窗口小部件的大小,即您的 Text()。


在我的情况下,对于一个大的字符串列表,这只是缩小了一些文本小部件。 - PhillauSofia

7
为了让此功能在使用行时正常工作,您需要将行子元素和DropdownButton的isExpanded属性都设置为Expanded:
Row(
    children: <Widget>[
      Expanded(
        child: DropdownButton<String>(
          isExpanded: true, 
          value: _selectedLanguageNative,
          items: list,
          hint: Text(LanguageLocal.getDisplayLanguage(
              Localizations.localeOf(context).languageCode)["name"]),
          onChanged: (String value) {
            print(value);
            setState(() {
              _selectedLanguageNative = value;
            });
          },
        ),
      ),
    ],
  ),

6
详细解释ganapat的回答,您可以按照以下方式设置列表:
final dropdownItems = _list
        .map((String item) => new DropdownMenuItem<String>(
              value: item,
              child: new SizedBox(width: 200.0, child: new Text(item)),
            ))
        .toList();

3

我曾经为这个问题苦苦挣扎,并最终找到了一个很好的解决方案,可以解决@Dah提出的问题。Github问题请看2019年9月18日中暗示了这一点:利用DropDownButton的selectedItemBuilder属性。 在这里Flutter Docs也有一个很好的工作示例。 我包括了我的代码片段,当DropdownButton关闭时,它简单地将省略号放在长文本的位置。它通过使用selectedItemBuilder返回的Text()小部件来实现这一点,该小部件允许设置overflow属性来放置省略号:

child: DropdownButton<String>(
        isExpanded: true,
        hint: Text('Select Faculty'),
        value: selectedFaculty,
        underline: Container(
          height: 0,
        ),
        onChanged: (String value) async {
          selectedFaculty = value;
        },
        selectedItemBuilder: (BuildContext context) {
          return dropFaculty.map<Widget>((String text) {
            return Text(
              text,
              overflow: TextOverflow.ellipsis,
            );
          }).toList();
        },
        items: disableKeyFields || selectedInstitution == null
            ? null
            : dropFaculty.map((String faculty) {
                return DropdownMenuItem<String>(
                  value: faculty,
                  child: Text(
                    faculty,
                    style: TextStyle(color: Colors.black),
                  ),
                );
              }).toList(),
      ),

1
感谢brianegan的回答。
在第三步之后,查看_DropdownMenuRouteLayout类:
  @override
  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
    // The maximum height of a simple menu should be one or more rows less than
    // the view height. This ensures a tappable area outside of the simple menu
    // with which to dismiss the menu.
    //   -- https://material.io/design/components/menus.html#usage
    final double maxHeight =
        math.max(0.0, constraints.maxHeight - 2 * _kMenuItemHeight);
    // The width of a menu should be at most the view width. This ensures that
    // the menu does not extend past the left and right edges of the screen.
    final double width = math.min(constraints.maxWidth, buttonRect.width);
    return BoxConstraints(
      minWidth: width,
      maxWidth: width,
      minHeight: 0.0,
      maxHeight: maxHeight,
    );
  }

您可以按照自己的方式实现“maxWith”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接