I have a layout with three Slivers:
- A
SliverAppBar
(not pinned). - A second
SliverAppBar
(pinned, containing aSearchBar
). - A
SliverList
.
The issue is with the second SliverAppBar
. I want it to be pinned below the status bar, respecting the safe area, but currently, it goes under the status bar, as shown in the second image:
The first drawing represents the default state on launch.
When scrolling, the first SliverAppBar
disappears as expected, and the second SliverAppBar
(with the SearchBar
) gets pinned at the top.
However, I want the second SliverAppBar
to stop just below the status bar instead of going behind it.
What I’ve Tried:
-
Wrapping the entire
CustomScrollView
in aSafeArea
:- This also shifts my first
SliverAppBar
, which I don’t want.
- This also shifts my first
-
Wrapping only the second
SliverAppBar
in aSafeArea
orSliverSafeArea
:- This adds padding between the two
SliverAppBars
, which is not the desired behavior.
- This adds padding between the two
-
Dynamic padding using a
SliverPersistentHeader
and working with itsshrinkOffset
:- However,
shrinkOffset
starts growing only when the header reaches the top of the screen, so it doesn’t solve the problem.
- However,
-
Using only 1
SliverAppBar
with aflexibleSpace
and abottom
:- This is close to the result I need but I didn’t manage to get the proper sizes.
Sample code
class MyPage extends StatefulWidget {
const MyPage({super.key});
@override
State<MyPage> createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
late ScrollController _scrollController;
@override
void initState() {
_scrollController = ScrollController();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: PrimaryScrollController(
controller: _scrollController,
child: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: false,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
background: Image.network(
'https://picsum.photos/250?image=9',
fit: BoxFit.cover,
),
),
),
const SliverAppBar(
pinned: true,
flexibleSpace: Padding(
padding: EdgeInsets.only(top: 12.0),
child: SearchBar(),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ListTile(
title: Text('Item $index'),
);
},
childCount: 20,
),
),
],
),
),
);
}
}
How can I achieve this behavior? Is there a proper way to pin a SliverAppBar
while respecting the safe area for just that Sliver?
2
Answers
1、try this:
2、NSliverPersistentHeaderBuilder
Let both the shrinkage area and the search bar be implemented with SliverPersistentHeader:
}