Flutter ListView.Builder() in scrollable Column with other widgets
Asked Answered
S

18

142

I have a TabBarView() with an amount of different views. I want of them to be a Column with a TextField at top and a ListView.Builder() below, but both widgets should be in the same scrollable area (scrollview). The way I implemented it threw some errors:

@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)
    ],
   );
}

Error:

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.

I read about stacking the ListView.builder() in an Expanded-Area but it made the textfield kind of "sticky" which is not what I want. :-)

I also came across CustomScrollView but didn't fully understand how to implement it.

Scabby answered 11/6, 2018 at 9:6 Comment(1)
Wrap the ListView with an Expanded widgetHamamelidaceous
V
294

Here is the solution:

SingleChildScrollView(
        physics: ScrollPhysics(),
        child: Column(
          children: <Widget>[
             Text('Hey'),
             ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemCount:18,
                itemBuilder: (context,index){
                  return  Text('Some text');
                })
          ],
        ),
      ),
Vandal answered 6/11, 2019 at 7:58 Comment(7)
it doesn't worked exacly for me, i needed to add the attribute for the columnMainAxisSize.min to make it work.Reardon
@PedroMolina Did you added shrinkWrap: true inside your ListView because it's working without it. However Thanks to @SunpreetSinghProspective
But with this solution NotificationListener not workingNonu
Adding shrinkwrap : true might degrade performance of your application. Check the video here api.flutter.dev/flutter/widgets/ScrollView/shrinkWrap.htmlNoonan
If adding shrinkwrap makes it slow, what is the simplest recommendation?Wyly
this code is wrongGullett
my use case was that listview builder is inside single child scroll view, so once content above listview goes out of screen, couldnt pull it back, this combination of ScrollPhysics and NeverScrollableScrollPhysics is exactly what I was looking for. ThanksLoy
F
156

Placing the ListView inside an Expanded widget should solve your problem:

@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))
    ],
   );
}
Falcon answered 12/6, 2018 at 8:13 Comment(3)
As far as I understood, Expanded and ListView work together, because expanded uses the rest of the screen. So if you want a button below the Expanded, you have first the padded Textfield then Expanded (which is scrollable) and then the Button in the bottom of your scren.Pawnshop
If having problem with this solution, verifiy if the column is inside a Positioned. If so, you need to specify top and bottom. So the dart can calculate the size. Specifying only top will cause out of data to calculate.Adventist
This is the right way and should be the accepted answer as it fullfills the usecase where we don't want whole page to scroll but only the listview to scroll.Bloodcurdling
B
72

Use SingleChildScrollView which allows the child widget to scroll

Solution

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

Two properties used here

shrinkWrap: true

only occupies the space it needs (it will still scroll when there more items).

physics: NeverScrollableScrollPhysics()

Scroll physics that does not allow the user to scroll. Means only Column+SingleChildScrollView Scrolling work.

Boarfish answered 21/6, 2020 at 5:1 Comment(2)
thanks and please don't make the same mistake as me as I left Expanded over ListView.builder and finally realised after 15 minutes of frustrationLiberec
physics: NeverScrollableScrollPhysics() solved my problem!Dahlberg
E
35

Reason for the error:

Column expands to the maximum size in main axis direction (vertical axis), and so does the ListView

Solution

You need to constrain the height of the ListView, so that it does expand to match Column, there are several ways of solving this issue, I'm listing a few here:


  1. If you want to allow ListView to take up all remaining space inside Column use Flexible.

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

  1. If you want to limit your ListView to certain height, you can use SizedBox.

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

  1. If your ListView is small, you may try shrinkWrap property on it.

    Column(
      children: <Widget>[
        ListView(
          shrinkWrap: true, // use it
        )
      ],
    )
    
Esteresterase answered 24/10, 2019 at 11:14 Comment(4)
Number 3 saved my problem. Thanks, great answer!Anetteaneurin
I am getting Incorrect use of ParentDataWidget. with Method 1Cara
@PiyushChauhan You might have missed to include Column. if that's not the case, please ask a separate question and I'll be glad to assist you.Esteresterase
I had no Flexible widget around my Listview--just an Expanded widget around the portion of the Column it was the descendant of. This really helped me, thanks!Ovine
S
22

Use physics: NeverScrollableScrollPhysics() and shrinkWrap: true inside ListView.Builder() and enjoy

Sandstone answered 25/7, 2020 at 3:0 Comment(0)
S
17

Here is an efficient solution:

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'));
            },
          ),
        ),
      ],
    );
  }
}

Here is a preview on dartpad.

You can use a SliverToBoxAdapter for the other children as only Slivers can be a direct child of a CustomScrollView.

If all the list items are the same height, then you could use SliverFixedExtentList, which is more efficient because the height of each child isn't calculated on the fly, but you will have to know the exact pixel height. You could also use a SliverPrototypeExtentList, where you provide the first item in the list(the prototype), and all the other children will use the height of the prototype so you don't need to know the exact height in pixels.

Snaky answered 17/12, 2020 at 12:4 Comment(1)
This is the best solution. NeverScrollablePhysics() and shrinkwrap: true totally defeat the purpose of ListView.builder - which is to optimize performance of huge lists. With SliverList and CustomScrollView you get the blazing performance of the list builder inside of another scrolling body... Like if you have a bunch of form elements that generate a long list below, and you need everything to scroll together.Cunnilingus
B
9

Use Expanded widget to constrain without overflowing those pixels, :)

Column(
  children: <Widget>[
    Expanded(
      child: ListView(),
    ),
    Expanded(
      child: ListView(),
    ),
  ],
)
Beaufort answered 28/11, 2019 at 11:23 Comment(0)
M
9

just add

Column(
mainAxisSize: MainAxisSize.max, //Add this line onyour column
children:[
    SomeWidget(),
    Expanded(child:ListView.builder())
  ]
)
Mcneal answered 5/3, 2021 at 15:21 Comment(1)
adding MainAxisSize.max, should not affect since that is default behaviour of the Column.Vernavernacular
A
7
//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));}))                
                    ),]),
Adjure answered 15/1, 2021 at 5:58 Comment(0)
R
6

In my case with a future i did it like this:

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,
                    );
                }
              },
            ),
          ),
        ],
      ),
    ),
Reardon answered 8/7, 2020 at 9:43 Comment(0)
U
3

Just add physics: NeverScrollableScrollPhysics() in ListView.builder() so you can scroll

Uganda answered 29/1, 2020 at 13:5 Comment(1)
Please include more context in answersAdapter
G
2

The best way will be to make the column scrollable by making the column child of SingleChildScrollView and then assigning the same ScrollController to both the SingleChildScrollView and the ListView.builder. This will make the text field and the below ListView as scrollable.

Grote answered 24/7, 2019 at 10:22 Comment(0)
H
2

Add physics: NeverScrollableScrollPhysics() inside Listview.builder() method and the nested Listview will scroll

Halophyte answered 22/4, 2020 at 23:4 Comment(0)
P
2
  body:  SingleChildScrollView(
    physics: ScrollPhysics(),
    child: Column(
          children: [
            getFiltersOnHomePage(),

            SizedBox(
              child: StreamBuilder(
                stream: FirebaseFirestore.instance.collection('posts').snapshots(),
                builder: (context,
                    AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
                  if (snapshot.connectionState == ConnectionState.waiting) {
                    return const Center(
                      child: CircularProgressIndicator(),
                    );
                  }
                  return ListView.builder(
                      physics: NeverScrollableScrollPhysics(),
                      shrinkWrap: true,
                      itemCount: snapshot.data!.docs.length,
                      itemBuilder: (ctx, index) => Container(
                            margin: EdgeInsets.symmetric(
                              horizontal: width > webScreenSize ? width * 0.3 : 0,
                              vertical: width > webScreenSize ? 15 : 0,
                            ),
                            child: PostCard(
                              snap: snapshot.data!.docs[index].data(),
                            ),
                          ));
                },
              ),
            ),
           

          ],
        ),
  ),[enter image description here][1]***You will be able to scroll through the page by using Expanded Widget

Blockquote Using this you can scroll over the entire page. This page includes a row and a listview builder inside a scrollable column.


Pipistrelle answered 8/10, 2022 at 6:21 Comment(0)
S
1

Column is not scrollable, which is why the TextField on top wouldn't scroll but the ListView on the bottom would.

The best way to solve this in my opinion is to make your TextField the first item in your ListView.

So you won't need a column, your parent widget is the ListView, and its children are the TextField followed by the remaining items you build with _postBuilder.

Skolnik answered 11/6, 2018 at 9:17 Comment(0)
R
1
return Column(
         children: [
           Text("Popular Category"),
            Expanded(
         child: ListView.builder(`enter code here`
             shrinkWrap: false,
             scrollDirection: Axis.horizontal,
             itemCount: 3,
             itemBuilder: (context, index) {
               return Row(
                 crossAxisAlignment: CrossAxisAlignment.start,
                 children: [
                   Text("hello"),
                 ],
               );
             }),   
           ),
         ],    
     );
Renunciation answered 12/2, 2021 at 18:53 Comment(0)
R
1

In my case, I added a Container with transparent color and height up to 270 to solve this one.

Column(
  children: <Widget>[
    ListView(
      shrinkWrap: true, // use it
    ),
    Container(
      color: Colors.transparent,
      height: 270.0,
    ),
  ],
)
Retinoscopy answered 2/11, 2022 at 13:21 Comment(0)
M
1

Just Use physics inside listview Builder and then it will be work as scrollable.

ListView.builder(
    physics: ScrollPhysics(),  // add this
    shrinkWrap: true,
    itemCount: rentalTypes.length,
    itemBuilder: (BuildContext context,index){
        String details = rentalTypes[index]['houseDetails'];
        String rent = rentalTypes[index]['rent'];
        return PostItem(image: AssetImage("assets/images/bedroom.jpg"), description: details, price: rent);
    }
)
Medicare answered 12/9, 2023 at 6:54 Comment(1)
Not working for me. I get the error "The overflowing RenderFlex has an orientation of Axis.vertical." and the screen indicates "Bottom overflowed".Trapeziform

© 2022 - 2024 — McMap. All rights reserved.