在带有其他小部件的可滚动列中使用Flutter ListView.Builder()

137

我有一个TabBarView(),里面包含不同的视图。我想要其中的一个视图是一个顶部有TextField,下面有ListView.Builder()的Column,并且这两个小部件都在同一个可滚动区域(ScrollView)内。但是我实现的方式出现了一些错误:

@override
Widget build(BuildContext context) {
return new Column(
  mainAxisAlignment: MainAxisAlignment.center,
    mainAxisSize: MainAxisSize.max,
    children: <Widget>[
      new Padding(
          padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
          child: new TextField(
            decoration: new InputDecoration(
                hintText: "Type in here!"
            ),
          )
      ),
      new ListView.builder(
          itemCount: _posts.length, itemBuilder: _postBuilder)
    ],
   );
}

错误:

I/flutter (23520): The following assertion was thrown during performResize():
I/flutter (23520): Vertical viewport was given unbounded height.
I/flutter (23520): Viewports expand in the scrolling direction to fill their container.In this case, a vertical
I/flutter (23520): viewport was given an unlimited amount of vertical space in which to expand. This situation
I/flutter (23520): typically happens when a scrollable widget is nested inside another scrollable widget.
I/flutter (23520): If this widget is always nested in a scrollable widget there is no need to use a viewport because
I/flutter (23520): there will always be enough vertical space for the children. In this case, consider using a Column
I/flutter (23520): instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
I/flutter (23520): the height of the viewport to the sum of the heights of its children.

我读到过在扩展区域中堆叠ListView.builder()的方法,但会导致文本字段有点“粘滞”,这不是我想要的。 :-)

我也尝试使用CustomScrollView,但不是很理解如何实现。


3
使用 Expanded 组件将 ListView 包裹起来。 - Vinoth Kumar
18个回答

284

以下是解决方案:

SingleChildScrollView(
        physics: ScrollPhysics(),
        child: Column(
          children: <Widget>[
             Text('Hey'),
             ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemCount:18,
                itemBuilder: (context,index){
                  return  Text('Some text');
                })
          ],
        ),
      ),

它对我来说并没有完全起作用,我需要添加columnMainAxisSize.min属性才能使其正常工作。 - Pedro Molina
4
你是否在你的ListView中添加了 shrinkWrap: true,因为没有它也能正常工作。不过还是感谢 @SunpreetSingh。 - Abhishek Kumar
但是使用这种解决方案,NotificationListener无法正常工作。 - Brutal
添加 shrinkwrap:true 可能会降低应用程序的性能。在此处检查视频 https://api.flutter.dev/flutter/widgets/ScrollView/shrinkWrap.html - Utsavkumar Lal
如果加入收缩包会使其变慢,最简单的建议是什么? - Wesley Barnes
显示剩余2条评论

148
将ListView放在Expanded小部件中应该可以解决您的问题:
@override
Widget build(BuildContext context) {
return new Column(
  mainAxisAlignment: MainAxisAlignment.center,
    mainAxisSize: MainAxisSize.max,
    children: <Widget>[
      new Padding(
          padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
          child: new TextField(
            decoration: new InputDecoration(
                hintText: "Type in here!"
            ),
          )
      ),
      new Expanded(child: ListView.builder(
          itemCount: _posts.length, itemBuilder: _postBuilder))
    ],
   );
}

2
据我所理解,Expanded和ListView是一起使用的,因为Expanded会利用屏幕上剩余的空间。所以如果您想在Expanded下方放置一个按钮,您需要首先添加一个带填充的TextField,然后是可滚动的Expanded,最后是位于屏幕底部的Button。 - NullPointer
1
如果使用此解决方案时遇到问题,请验证该列是否位于Positioned内。如果是,则需要指定顶部和底部,以便Dart可以计算大小。仅指定顶部将导致数据超出计算范围。 - Thiago Silva
3
这是正确的做法,应该被接受作为答案,因为它满足了我们不想让整个页面滚动而只想让列表视图滚动的用例。 - Dharmik Thakkar

70

使用 SingleChildScrollView 让子组件可以滚动。

解决方案

SingleChildScrollView(
          child: Column(
            children: <Widget>[
                      ListView.builder(
                      shrinkWrap: true,
                      physics: NeverScrollableScrollPhysics(),

这里使用了两个属性

shrinkWrap: true

只占用它需要的空间(如果有更多的项目,它仍然会滚动)。

physics: NeverScrollableScrollPhysics()

指用户不能滚动的滚动物理效果,只能使用Column+SingleChildScrollView进行滚动。


谢谢,请不要犯和我一样的错误,我把Expanded放在了ListView.builder上面,最终在沮丧了15分钟后才意识到。 - user2244131
physics: NeverScrollableScrollPhysics()解决了我的问题! - Alex Angelico

33

错误原因:

Column 在主轴方向(垂直轴)上会扩展到最大尺寸,ListView 也会如此。

解决方案:

您需要限制 ListView 的高度,以便它不会扩展到与 Column 相匹配。有多种解决此问题的方法,以下是其中一些:


  1. 如果您希望在 Column 内允许 ListView 占用所有剩余空间,请使用 Flexible

    Column(
      children: <Widget>[
        Flexible(
          child: ListView(...),
        )
      ],
    )
    

  1. 如果你想限制你的ListView的高度,你可以使用SizedBox

Column(
  children: <Widget>[
    SizedBox(
      height: 200, // constrain height
      child: ListView(),
    )
  ],
)

  1. 如果你的 ListView 很小,你可以尝试在它上面使用 shrinkWrap 属性。

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

3
第三个解决方案解决了我的问题。谢谢,非常好的答案! - victommasi
1
我使用方法1时出现了“Incorrect use of ParentDataWidget.”的错误。 - Piyush Chauhan
@PiyushChauhan 你可能忘记包含“Column”了。如果不是这种情况,请提出一个单独的问题,我很乐意帮助你。 - CopsOnRoad
我的ListView周围没有Flexible小部件,只有一个Expanded小部件包围着它所属的Column的一部分。这真的帮了我很多,谢谢! - Tristan Bennett

22

ListView.Builder() 中使用 physics: NeverScrollableScrollPhysics()shrinkWrap: true,就能愉快地使用啦!


16

这里有一个高效的解决方案:

class NestedListExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        const SliverToBoxAdapter(
          child: Text('Header'),
        ),
        SliverList(
          delegate: SliverChildBuilderDelegate(
            (ctx, index) {
              return ListTile(title:Text('Item $index'));
            },
          ),
        ),
      ],
    );
  }
}

这里有一个在dartpad上的预览

你可以使用SliverToBoxAdapter作为其它子项,因为只有Slivers可以成为CustomScrollView的直接子项。

如果所有列表项高度都相同,则可以使用SliverFixedExtentList,这是更有效率的方法,因为每个子元素的高度不是即时计算的,但您需要知道确切的像素高度。您也可以使用SliverPrototypeExtentList,在其中提供列表中的第一个项(原型),所有其他子项将使用原型的高度,因此您不需要知道像素高度的确切值。


5
这是最佳解决方案。NeverScrollablePhysics()shrinkwrap: true完全打败了ListView.builder的优化大型列表性能的目的。使用SliverList和CustomScrollView,您可以在另一个滚动体内获得列表构建器的惊人性能...就像如果您有一堆生成长列表的表单元素,并且您需要所有内容一起滚动。 - Matthew Rideout

8

只需添加

Column(
mainAxisSize: MainAxisSize.max, //Add this line onyour column
children:[
    SomeWidget(),
    Expanded(child:ListView.builder())
  ]
)

添加 MainAxisSize.max 不应该影响到 Column 的默认行为。 - Pedro Luz

8
使用Expanded小部件来约束这些像素,而不会溢出,:)
Column(
  children: <Widget>[
    Expanded(
      child: ListView(),
    ),
    Expanded(
      child: ListView(),
    ),
  ],
)

6
//If you want Listview.builder inside ListView and want to scroll the parent ListView// //whenever the Items in ListView.builder ends or start you can do it like this
       body: ListView(
                  physics: ScrollPhysics(),
                  children: [
                    SizedBox(height: 20),
                    Container( height: 110.0 *5, // *5 to give size to the container //according to items in the ListView.builder. Otherwise will give hasSize Error
                    child:ListView.builder(
                       physics: NeverScrollableScrollPhysics(),
                       scrollDirection: Axis.vertical,
                       itemCount: 5,
                        itemBuilder: (BuildContext context, int indexChild) {
                         return InkWell(child:Container(height:100));}))                
                    ),]),

6

在我的情况下,我这样做了:

SingleChildScrollView(
      physics: ScrollPhysics(),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Text("Hey ho let's go!"),
          
          Flexible(
            child: FutureBuilder(
              future: getData(),
              builder: (BuildContext context,
                  AsyncSnapshot<List<Sale>> snapshot) {
                

                if (snapshot.connectionState != ConnectionState.done ||
                    snapshot.hasData == null) {
                  
                    return CircularProgressIndicator();

                } else {
                  data = snapshot.data;
                  return ListView.builder(
                      physics: NeverScrollableScrollPhysics(),
                      shrinkWrap: true,
                      itemBuilder: (BuildContext context, int index) {
                        return dataItemWidget(size, data[index], context);
                      },
                      itemCount: data.length,
                    );
                }
              },
            ),
          ),
        ],
      ),
    ),

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