I have built a small test example for this issue I'm experiencing. You can see from the gif below that the Flutter app only has a header at the top. When I pull down to refresh, the header does not rebuild itself. However, when I push the header upwards, the header rebuilds itself numerous times. Also note that I have not called setState() anywhere in my code so I'm not sure how it knows it needs to rebuild itself when I push the scrollview upwards.
I would like to header to not rebuild itself at all as there is no reason it should rebuild itself. It is static / stateless and should not change at all. The size of the header should also not change (hence the expandedHeight and collapsedHeight are the same at 136.0).

Here is the example code I used for you to recreate this:
import 'package:meta/meta.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'dart:math' as math;
import 'dart:async';
class _TestHeader extends SliverPersistentHeaderDelegate {
_TestHeader({
@required this.collapsedHeight,
@required this.expandedHeight,
@required this.showHeading,
});
bool showHeading;
final double expandedHeight;
final double collapsedHeight;
@override
double get minExtent => collapsedHeight;
@override
double get maxExtent => math.max(expandedHeight, minExtent);
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
print("rebuilding headings");
return new SafeArea(
child: Column(children: <Widget>[
const SizedBox(height: 24.0),
new GestureDetector(
onTap: () {
},
child: new Container(
decoration: const BoxDecoration(
color: CupertinoColors.white,
border: const Border(
top: const BorderSide(color: const Color(0xFFBCBBC1), width: 0.0),
bottom:
const BorderSide(color: const Color(0xFFBCBBC1), width: 0.0),
),
),
height: 44.0,
child: new Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: new SafeArea(
top: false,
bottom: false,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
const Text(
'This is my heading',
style: const TextStyle(color: CupertinoColors.activeBlue, fontSize: 16.0),
)
],
),
),
),
),
),
]));
}
@override
bool shouldRebuild(@checked _TestHeader oldDelegate) {
// return false;
return expandedHeight != oldDelegate.expandedHeight ||
collapsedHeight != oldDelegate.collapsedHeight;
}
}
class TestHeaderPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new TestHeaderState();
}
}
class TestHeaderState extends State<TestHeaderPage> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return new CupertinoPageScaffold(
//i will need to convert this to a sliver list to make this work properly.
backgroundColor: const Color(0xFFEFEFF4),
navigationBar: new CupertinoNavigationBar(
middle: new Text('Test Headers'),
),
child: new SafeArea(
child: new CustomScrollView(slivers: <Widget>[
new CupertinoRefreshControl(onRefresh: () {
print("pulling on refresh");
return Future<void>(() {});
}),
new SliverPersistentHeader(
delegate: new _TestHeader(
collapsedHeight: 136.0,
expandedHeight: 136.0,
showHeading: true)),
]),
));
}
}
This is totally normal.
Your _TestHeader is not a widget. Just because it has the build method doesn't mean it's one. :)
You extended SliverPersistentHeaderDelegate, which is used to build SliverPersistentHeader.
The thing is : SliverPersistentHeader is not a widget either. It's a sliver, which is a different way to render things on screen.
And, in the case of SliverPersistentHeader, it is a specific kind of sliver that is rebuilt whenever the scroll offset change. That is, to potentially handle scroll specific animations.
Such as scroll up make the header disappear. And scroll down to make it snap back.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With