给定的垂直视口高度未被限定

334

这是我的代码:

@override
Widget build(BuildContext context) {
  return new Material(
    color: Colors.deepPurpleAccent,
    child: new Column(
     mainAxisAlignment: MainAxisAlignment.center,
        children:<Widget>[new GridView.count(crossAxisCount: _column,children: new List.generate(_row*_column, (index) {
          return new Center(
              child: new CellWidget()
          );
        }),)]
    )
  );
}

以下是异常信息:

I/flutter ( 9925): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter ( 9925): The following assertion was thrown during performResize():
I/flutter ( 9925): Vertical viewport was given unbounded height.
I/flutter ( 9925): Viewports expand in the scrolling direction to fill their container.In this case, a vertical
I/flutter ( 9925): viewport was given an unlimited amount of vertical space in which to expand. This situation
I/flutter ( 9925): typically happens when a scrollable widget is nested inside another scrollable widget.
I/flutter ( 9925): If this widget is always nested in a scrollable widget there is no need to use a viewport because
I/flutter ( 9925): there will always be enough vertical space for the children. In this case, consider using a Column
I/flutter ( 9925): instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
I/flutter ( 9925): the height of the viewport to the sum of the heights of its children.
I/flutter ( 9925): 
I/flutter ( 9925): When the exception was thrown, this was the stack:
I/flutter ( 9925): #0      RenderViewport.performResize.<anonymous closure> (package:flutter/src/rendering/viewport.dart:827:15)
I/flutter ( 9925): #1      RenderViewport.performResize (package:flutter/src/rendering/viewport.dart:880:6)
I/flutter ( 9925): #2      RenderObject.layout (package:flutter/src/rendering/object.dart:1555:9)

您也可以使用简单的容器,而不是Column(主要用于多个子小部件)。 - Napolean
1
小部件构建(BuildContext context) { 返回新的材料( 颜色:Colors.deepPurpleAccent, 孩子:新容器( 对齐方式: 对齐.中心, 孩子: 新网格视图计数(crossAxisCount:_column,children:新列表生成(_row*_column, (索引) { 返回新中心( 孩子: 新单元小部件() ); }) ), ) ); --此代码也不能帮助@Napolean - pallav bohara
尝试在Column下添加mainAxisSize到max,并查看是否有所帮助。 - pblead26
27个回答

727

加上这两行代码

    ListView.builder(
        scrollDirection: Axis.vertical,
        shrinkWrap: true,
    ...

在这段代码片段中,有一个带有以下属性的ListView.builder
  1. scrollDirection: Axis.vertical:此属性指定列表的滚动方向。在本例中,它设置为Axis.vertical,这意味着列表将垂直滚动。如果您想让列表水平滚动,也可以将其设置为Axis.horizontal

  2. shrinkWrap: true:默认情况下,ListView沿其主轴尽可能占用空间。但是,当您将shrinkWrap设置为true时,ListView将尝试包装其内容并仅占用所需的空间。当您想在另一个可滚动视图内包含ListView或列表大小不确定时,这可能会有所帮助。


42
谢谢,您可以将ListView用Flexible包裹以使其可滚动。 - Shady Mohamed Sherif
9
这是shrinkWrap的文档:https://api.flutter.dev/flutter/widgets/ScrollView/shrinkWrap.html shrinkWrap的作用是根据其内容自动调整其大小,以避免滚动视图无限制地增长。 - Omer Celik
17
请注意,根据文档,如果列表视图中有很多小部件,则将 shrinkWrap 设置为 true 可能会显着增加成本。请参阅文档中的以下代码片段:将滚动视图的内容收缩包装比扩展到允许的最大尺寸要昂贵得多,因为内容在滚动期间可以扩展和收缩,这意味着每当滚动位置更改时都需要重新计算滚动视图的大小。 - Nirvan Nagar
9
shrinkWrap: true 这个属性很耗费资源,导致我的列表卡顿,但是通过将 ListView 包裹在 Flexible 中解决了这个问题。 - RukshanJS
@BloodLoss 我使用了 shrinkWrap: true,我的问题得到了解决,但是 listView 上的滚动不起作用... 为什么呢? - benten
ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true, ) - benten

307

当您尝试在Column中使用ListView/GridView时,通常会发生这种情况。有很多解决方法,我在此列出了几种。

  1. ListView包装在Expanded小部件中

Column(
  children: <Widget>[
    Expanded( // wrap in Expanded
      child: ListView(...),
    ),
  ],
)
  • ListView包装在SizedBox中,并给定有限的height

  • Column(
      children: <Widget>[
        SizedBox(
          height: 400, // fixed height
          child: ListView(...),
        ),
      ],
    )
    
  • ListView中使用shrinkWrap: true

  • Column(
      children: <Widget>[
        ListView(
          shrinkWrap: true, // use this
        ),
      ],
    )
    

    11
    尝试在垂直列表视图中包含水平列表视图。只有sizedbox方法可行,但高度太过死板。还有其他方法吗? - fractal
    4
    shrinkWrap: true 完成了任务。 - Ali80
    1
    这正是我正在寻找的。您有没有可能添加每个选项的优缺点? - emragins
    2
    这是最好的答案。只需注意,如果ListView中有很多元素,并且shrinkWrap设置为true,则它会显着增加成本。摘自文档:“收缩滚动视图内容的包裹比扩展到允许的最大尺寸要显着更昂贵,因为内容在滚动期间可以扩展和收缩,这意味着每当滚动位置更改时都需要重新计算滚动视图的大小。” - Nirvan Nagar
    @CopsOnRoad 我使用shrinkWrap: true,问题已解决,但是listView上的滚动不起作用..有任何想法吗? - benten
    显示剩余7条评论

    56

    所以,每个人都发了答案,但是没有人关心解释为什么:
    我将复制有关shrinkWrap的文档:

    滚动视图在[scrollDirection]中的范围是否应该由正在查看的内容确定。

    如果滚动视图不收缩,则滚动视图将扩展到[scrollDirection]中允许的最大尺寸。 如果滚动视图在[scrollDirection]中具有无限制的约束,则必须为[shrinkWrap]设置为true。

    相对于扩展到允许的最大大小,收缩滚动视图的内容要显着更昂贵,因为内容可以在滚动期间扩展和收缩,这意味着每当滚动位置更改时都需要重新计算滚动视图的大小。

    默认为false

    所以,根据文档的词汇,这里正在发生的是我们的ListView处于一个无限制的约束的情况下(在我们滚动的方向上),因此ListView会抱怨:

    ...竖直视口被赋予了无限的竖直空间来扩展。

    简单地将shrinkWrap设置为true,将确保它包装由内容定义的大小。 一个用于说明的示例代码:

    // ...
      ListView(
        // Says to the `ListView` that it must wrap its
        // height (if it's vertical) and width (if it's horizontal).
        shrinkWrap: true,
      ),
    // ...
    

    这就是你的代码出了问题的原因,然而,@Rémi 的建议对于这种情况最好,使用Align代替Column


    41

    将网格视图放在FlexibleExpanded小部件内

    return new Material(
        color: Colors.deepPurpleAccent,
        child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children:<Widget>[
             Flexible(
              child:  GridView.count(crossAxisCount: _column,children: new List.generate(_row*_column, (index) {
              return new Center(
                 child: new CellWidget(),
              );
            }),))]
        )
    );
    

    2
    在GridView中添加shrinkWrap: true以扩展并使其适应内容。 - Wilmer

    36

    Viewport异常的原因

    GridView / ListView增长,直到限制(限制)停止这种扩展并滚动以查看超出该大小的项目。

    但是,Column在没有任何约束条件的情况下布置其子项,完全忽略其自身的大小和屏幕大小。 在Column内部存在无限空间。

    Viewport was given unbounded height异常是由于Grid/ListViewColumn中无限扩展。

    Flexible/Expanded存在是为了基于Column's大小给Column子项提供约束条件。(除了在RowColumn内部,这两个小部件不能在其他地方使用。)

    Column中的Flexible/Expanded小部件中,Grid/ListView仅使用未被其他非灵活小部件占用的剩余空间进行布局,这些小部件首先进行布局。(有关布局阶段信息,请参见底部。)

    shrinkWrap不是一个好的解决方案

    Column内部使用shrinkWrap: true on ListView不是真正有用的,因为:

    • ListView将不再滚动
    • ListView仍然可能会溢出

    足够高以显示其所有项目的ListView将不会滚动。可以说这违背了使用ScrollView小部件(ListView的父类)的目的。

    Column布局阶段1中(有关布局阶段的解释,请参见底部),ListView可以具有任何高度(没有约束条件)。带有shrinkWrap:true属性的ListView将增长高度以显示其所有项。使用足够的项目,shrinkWrappedListView将继续增长(它永远不会滚动)以溢出包含它的Column, 可能会超出屏幕或其他较小的限制。

    难道shrinkWrap不应该使ListView的大小仅为其项目的大小剩余空间(最多达到屏幕高度),然后滚动吗?

    这听起来很直观,但是在第1阶段中,在Column内部进行布局时,无限空间中进行布局

    因此,剩余的空间是无限/无穷的。 并没有达到最大高度。 shrinkWrap:true只是随着项目的添加而保持增长Column的高度,直到溢出屏幕(或其他较小的约束)。

    shrinkWrap:true溢出示例

    这是将项目添加到Shrinkwrapped ListView的示例,直到其高度高于屏幕,创建溢出警告区域:

    (只需继

    import 'package:flutter/material.dart';
    
    class ColumnListViewPage extends StatefulWidget {
      @override
      _ColumnListViewPageState createState() => _ColumnListViewPageState();
    }
    
    class _ColumnListViewPageState extends State<ColumnListViewPage> {
      int _count = 0;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Column & ListView'),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              setState(() {
                _count++;
              });
            },
          ),
          body: SafeArea(
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(color: Colors.red)
              ),
              /// // without center, Column will be as narrow as possible
              alignment: Alignment.center,
              /// Column is forced (constrained) to match screen dimension,
              child: Column(
                children: [
                  Text('Inner (Nested) Column'),
                  ListView.separated( // ← should be wrapped in Expanded
                    itemCount: _count,
                    separatorBuilder: (context, index) => const Divider(),
                    itemBuilder: (context, index) => Padding(
                      padding: const EdgeInsets.symmetric(vertical: 38.0),
                      child: Text('Row $index'),
                    ),
                    shrinkWrap: true, // should be removed
                  )
                ],
              ),
            ),
          ),
        );
      }
    }
    
    对于在Column中的GridView/ListView,使用ExpandedFlexible进行包装可能是最安全的解决方案。

    Expanded / Flexible

    ExpandedFlexible只能在Column(以及其类似的兄弟组件如Row等)中使用。

    它们必须作为Column的直接子组件使用。不能将Expanded/Flexible“嵌套”在SizedBox或其他小部件中。只能直接放在Column下面作为直接子组件。

    Column内部,将ListView/GridView放置在ExpandedFlexible中可以确保它仅使用可用空间,而不是无限空间。

    Column
    → Expanded
      → ListView
    
    Column中的Expanded/Flexible包含的ListView/GridView不需要shrinkWrap,因为它们受到Expanded/Flexible小部件的约束(给定最大高度)。因此当使用完该约束/定义的高度时,ListView/GridView将停止增长并开始滚动。 Column将子项分两个阶段进行布局: 1.首先在没有任何限制的情况下(不受限空间)进行布局。 2.然后基于Column父级中剩余的空间进行布局。
    第一阶段: 任何不在FlexibleExpanded内的Column子项都将在第一阶段中布置,即在无限、无限制的空间中(完全忽略屏幕尺寸)。 需要垂直边界/约束来限制其增长的小部件(例如ListView)会在第一阶段中导致Vertical viewport was given unbounded height异常,因为在无限空间中没有垂直边界。 大多数小部件都有固有大小。例如,Text仅与其内容和字体大小/样式一样高。它不会尝试填充空间。定义高度的小部件在Column布局的第一阶段中表现良好。
    第二阶段: 将会增长以填充边界的任何小部件都应放在FlexibleExpanded中进行第二阶段布局。 第二阶段从其父约束中减去在第一阶段使用的空间计算Column的剩余空间。该剩余空间被提供给第二阶段的子元素作为约束条件。 例如,ListView将仅增长以使用剩余空间并停止,避免视口异常。
    关于Column布局算法和其中Expanded的更多信息请参见这里

    2
    非常感谢您提供这么详细的解释。 - Chuck Batson
    1
    感謝你在這方面的努力,真的很有幫助。 - K-Soliman

    36
    尽管shrinkWrap可以实现功能,但在ListView中无法滚动。
    如果你想要滚动功能,可以添加physics属性。
    ListView.builder(
        scrollDirection: Axis.vertical,
        shrinkWrap: true,
        physics: ScrollPhysics(),
    ...
    

    当你想要将滚动范围扩展到屏幕上的其他小部件时,这也是非常有帮助的。
    Column(
      children: [
        ListTile(
        //Some other functionality
        ),
        ListView.builder(
          shrinkWrap: true,
          itemCount: 10,
          physics: ScrollPhysics(),
          // Some other functionality
        ),
      ],
    ),
    

    17

    当可滚动的小部件嵌套在另一个可滚动的小部件中时,通常会出现这种情况。

    在我的情况下,我在Column内使用GridView,然后就会抛出这个错误。

    GridView小部件有一个名为shrinkWrap的属性/命名参数,将其设置为true即可解决我的问题。

    GridView.count(
      shrinkWrap: true,
      // rest of code
    )
    

    我使用了shrinkWrap: true,我的问题得到了解决,但是listView上的滚动不起作用..为什么? - benten

    12
    不要使用Column用于单个子元素的对齐,应改用Align
    new Align(
      alignment: Alignment.topCenter,
      child: new GridView(),
    )
    

    尽管它解决了我的错误,但我仍然无法垂直居中我的GridView。 它显示与之前相同。 尝试了所有的Alignment标志。 我错过了什么? - pallav bohara

    10

    有很多答案,但每个答案都有自己的限制。 每个人都在说要使用shrinkWrap: true。是的,你应该使用它,但这些代码的效果是当你滚动列表时,它会自动移动到顶部项目,也就是回滚到顶部。 因此,完美的解决方案是使用带有物理效果的shrinkwrap,这样列表才能平滑地滚动。

          shrinkWrap: true,          //
          physics: ScrollPhysics(),  // both line is mandatory
    

    如果你想看到Listview.builder的其他示例,其中包含列作为子元素且列表可以垂直滚动,则你可以在这里看到示例 - 排列多个文本以形成垂直列表。


    8

    增加下面这两行代码

    ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true,

    ...


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