Optimizing Flutter: The Importance of Placing a List Widget into the Header with NestedScrollView

Matatias Situmorang
3 min readSep 25, 2023

Remove gaps/empty spaces between scrollable widgets and headers on NestedScrollView widgets.

Photo by Jose Fabula on Unsplash

In my previous article “Dynamic Header in Flutter App”, I explored about NestedScrollWidget to create a dynamic header in Flutter App (see image below).

There doesn’t seem to be anything wrong with the code or the UI. This UI is built with NestedScrollView. In the header I use SliverAppbar and in the body, I use the ListView widget. I haven’t noticed anything wrong since it’s running on Android. Until then when I ran my App on iOS, I discovered there was unnecessary padding between the header and widget body.

I made some changes to debug the code. Setting padding to zero, removing margins, reducing borderRadius, and many more. However, the gaps between the body and the header still exist. See the image below, I created a minimum view for debugging:

Gaps between SliverAppbar and Listview

and here’s the code:

@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
// appBar: AppBar(title: const Text("appbar")),
body: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) => [
const SliverAppBar(
backgroundColor: Colors.grey,
title: Text('sliverappbar'),
)
],
body: ListView.builder(
itemBuilder: (context, index) => Card(
color: Colors.amber,
child: ListTile(title: Text('$index')),
),
itemCount: 20,
),
));
}

I also tried setting the AppBar from Scaffold to make sure the gap wasn’t caused from there. See image below:

Appbar and SliverAppbar

After seeing these results, I assume this gap is caused by the appbar. Then I raised this issue to the Flutter repository on GitHub: Issue #134514

And thankfully, flutter has a large and supportive community. Now I was able to solve this issue. From the documentation here link, the example given is using a CustomScrollView which does not have a body property. So, it is very logical to directly put a scrollable list inside the header.

If we follow the example in the documentation and implement it to NestedScrollView, then we need to move ListView.builder from body to headerSliverBuilder.

But since ListView.builder cannot be used inside headerSliverBuilder, we can change it with the SliverList widget.

the code will be:

@override
Widget build(BuildContext context) {
return Scaffold(
// backgroundColor: Colors.grey,
// appBar: AppBar(title: const Text("appbar")),
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) => [
const SliverAppBar(
pinned: true,
floating: true,
backgroundColor: Colors.grey,
title: Text('sliverappbar'),
),
// cant use ListView here, it will throw exception
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Card(
color: Colors.amber,
child: ListTile(title: Text('$index')),
),
childCount: 20),
)
],
body: SizedBox() // or you can use another scrollable widget here

and final result:

Final result

Lastly, since we don’t need a nested scroll widget, we can actually modify them NestedscrollView with a CustomScrollView. So there is no need to define the widget body anymore.

🙏Thank you for having the end. Don’t forget to clap 👏 if you like this article. 😃

Reach me out on X: Matatias Situmorang💙 (@pmatatias_) / X (twitter.com)

--

--

Matatias Situmorang

Flutter developer | Blogger | Reach me on twitter @pmatatias_