I'm having an issue using a NestedScrollView with two SliverAppBars - one pinned and one not pinned. In the non-pinned app bar I'd like to put a CupertinoSlidingSegmentedControl
(tab selector of a sort, that also scrolls with the page). Then inside the body of the scroll view, I'd like to put a PageView or similar, where I can switch between the pages, based on the segmented control's selection.
I've also set
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
inside main()
.
Here's my code:
class MyNestedScrollView extends StatefulWidget {
const MyNestedScrollView({super.key});
@override
State<StatefulWidget> createState() => _MyNestedScrollViewState();
}
class _MyNestedScrollViewState extends State<MyNestedScrollView> {
final GlobalKey<NestedScrollViewState> _nestedScrollViewKey = GlobalKey();
int segmentedControlValue = 0;
var overlapAbsorberHandle = SliverOverlapAbsorberHandle();
Widget _buildSegmentedControl() {
return CupertinoSlidingSegmentedControl<int>(
children: const {
0: Text('First'),
1: Text('Second'),
},
onValueChanged: (int? newValue) {
setState(() {
segmentedControlValue = newValue ?? 0;
});
},
groupValue: segmentedControlValue,
);
}
Widget _buildPage(int pageIndex, String title) {
return Builder(builder: (context) {
return CustomScrollView(
key: PageStorageKey<String>('page$pageIndex'),
slivers: [
// Add the Overlap Injector to get the correct overlap
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
SliverToBoxAdapter(
child: Container(
color: Colors.green,
child: const TextField(
decoration: InputDecoration(
hintText: 'Search',
prefixIcon: Icon(Icons.search),
),
),
),
),
SliverList.builder(
itemBuilder: (context, index) => ListTile(
title: Text('$title $index'),
),
),
],
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
key: _nestedScrollViewKey,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
// Add the Overlap Absorber widget to the SliverAppBar
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: const SliverAppBar(
pinned: true,
backgroundColor: Colors.blue,
title: Text('Pinned AppBar'),
),
),
SliverPadding(
padding: const EdgeInsets.only(top: 56 + 24),
sliver: SliverAppBar(
pinned: false,
backgroundColor: Colors.yellow,
flexibleSpace: FlexibleSpaceBar(
background: _buildSegmentedControl(),
),
),
),
];
},
body: IndexedStack(
index: segmentedControlValue,
children: [
_buildPage(0, 'First Page'),
_buildPage(1, 'Second Page'),
],
),
),
);
}
}
Target layout: Target layout
Current layout: Current laoyt
I tried various approaches using SliverOverlapAbsorber
, but none succeeded. Without it, whenever I start typing in the text field, it jumps behind the app bar (a big no-no). Whenever I add the absorber and an injector, I get this empty space as seen on the screenshots bellow. How do I fix that?