Flutter

ListView inside SingleChildScrollView in Flutter

In flutter application development there can be some scenarios for developers, where they have to use ListView inside SingleChildScrollView. This can be a bit tricky for new developers. They can face some issues while implementing this type of User Interface. In this tutorial we will learn how to use ListView inside SingleChildScrollView in flutter. And how to avoid the problems that we can face in implementing this.

Scenarios of ListView inside SingleChildScrollView.

Let’s discuss some scenarios where we have to use ListView inside SingleChildScrollView.

If we need to combine a list of items with other widgets in a single scrollable view, using a ListView inside a SingleChildScrollView allows us to achieve this. We can have other widgets placed above or below the ListView and have them scroll together seamlessly.

In some cases, we may have a requirement to nest multiple scrollable widgets. For example, if we have a ListView inside a PageView. Which is already scrollable horizontally, we need to wrap the ListView with a SingleChildScrollView to handle vertical scrolling separately. This allows us to have nested scrolling behaviours within our app.

Implementation of ListView inside SingleChildScrollView.

Let’s dive into code and see how to achieve this kind of user interface.

This will be our output

ListView inside singleChildScrollView
ListView inside singleChildScrollView

As you can see in output that we have a header (above of the ListView) and footer (below of the ListView). It means that there are three widgets in a column (A container on top of ListView, then ListView and another container at bottom of ListView). First we will place all these three widgets inside a column and put this column inside the SingleChildScrollview.

class ScrollingScreen extends StatelessWidget {
    ScrollingScreen({Key? key}) : super(key: key);

  List<String> _data = [];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Scrolling Screen'),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            Container(
              height: 300,
              color: Colors.grey.withOpacity(0.2),
              child: const Center(
                child: Text('Header'),
              ),
            ),
            ListView.builder(
                itemCount: _data.length,
                itemBuilder: (context, position) {
                  return ListTile(title: Text(_data[position]));
                }),
            Container(
              height: 300,
              color: Colors.grey.withOpacity(0.2),
              child: const Center(
                child: Text('Footer'),
              ),
            ),
          ],
        ),
      ),
    );
  }

  List<String> getDataList() {
    List<String> data = [];
    for (int i = 0; i < 30; i++) {
      data.add('Item $i');
    }
    return data;
  }
}

In the above code, we first of all we create a stateless widget ScrollingScreen. This is our screen on which we want to display the header, footer and ListView. Inside that screen we use SingleChildScrollView as a parent of Column that contains ListView.

It looks fine and seems that this will work smooth when we will run this piece of code. But when we compile this code and install app in our device, we will face a problem with the widgets.

Problem in ListView in SingleChildScrollView

Vertical viewport was given unbounded height.

Viewports expand in the scrolling direction to fill their container. In this case, a vertical viewport was given an unlimited amount of vertical space in which to expand. This situation typically happens when a scrollable widget is nested inside another scrollable widget.

If this widget is always nested in a scrollable widget there is no need to use a viewport because there will always be enough vertical space for the children. In this case, consider using a Column or Wrap instead. Otherwise, consider using a CustomScrollView to concatenate arbitrary slivers into a single scrollable.

Solution

The solution of this error is to use shrinkwrap property of ListView. Simply set this value of shrinkWrap in ListView to get rid of this problem.

shrinkwrap :true

ShrinkWrap in ListView:

In Flutter, the shrinkWrap property of a ListView determines how the list should adjust its size when its content is smaller than the available space. When shrinkWrap is set to true, the ListView will shrink-wrap its content tightly and take up only the space required by its children.

The shrinkWrap property is particularly useful when you have a ListView inside a SingleChildScrollView or another widget with a fixed or limited height. It ensures that the ListView does not occupy unnecessary space and allows it to coexist within a limited vertical area.

Now run code again. Now we can see our output on screen. But still there will be a problem in the behavior of scroll of ListView. The ListView will not be scrolled yet.

To scroll the ListView smoothly, we will use the physics property of Listview. Set this value of physics in ListView.

physics: NeverScrollableScrollPhysics(),

physics:NeverScrollableScrollPhysics() in ListView

In Flutter, the physics property of a ListView controls the scrolling behavior of the list. When you set physics to NeverScrollableScrollPhysics(), it disables scrolling for the ListView and prevents any user-initiated scrolling or scrolling caused by other gestures.

The final code of ListView will be

ListView.builder(
    itemCount: _data.length,
    shrinkWrap: true,
    physics: NeverScrollableScrollPhysics(),
    itemBuilder: (context, position) {
      return ListTile(title: Text(_data[position]));
    }),

After adding this line of code, Now run code again. You can see that now its working smooth and fine. The above mentioned two problems are very common which can be faced. We have discussed their solutions to overcome that issues. Hopefully this will be helpful for your next development of complex designs in Flutter.

Leave a Reply

Your email address will not be published. Required fields are marked *