Optimizing Flutter: The Importance of Placing a List Widget into the Header with NestedScrollView
Remove gaps/empty spaces between scrollable widgets and headers on NestedScrollView widgets.
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:
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:
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:
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)