How to mask-out the overlaped section, visible through the "translucent header sliver" in the NestedScrollView?
580
How about using CustomClipper
for List itself? Because the list height is dynamic during scrolling, the clip height must be calculated dynamically. So I pass the clipHeight into the custom clipper.
To get the clipHeight, I use MediaQuery.of(context).size.height
- header height. So I create another class to get this value.
...
body: CustomWidget (
child: ListView.builder(
...
class CustomWidget extends StatelessWidget {
final Widget child;
CustomWidget({this.child,Key key}):super(key:key);
@override
Widget build(BuildContext context) {
return ClipRect(
clipper: MyCustomClipper(clipHeight: MediaQuery.of(context).size.height-200),
child: child,
);
}
}
class MyCustomClipper extends CustomClipper<Rect>{
final double clipHeight;
MyCustomClipper({this.clipHeight});
@override
getClip(Size size) {
double top = math.max(size.height - clipHeight,0) ;
Rect rect = Rect.fromLTRB(0.0, top, size.width, size.height);
return rect;
}
@override
bool shouldReclip(CustomClipper oldClipper) {
return false;
}
}
Author by
goodUser
Updated on December 24, 2022Comments
-
goodUser over 1 year
The following code yields a scrollable list together with a "translucent pinned sliver header".
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: NestedScrollView( headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return [ SliverPersistentHeader( delegate: _SliverPersistentHeaderDelegate(), pinned: true, ), ]; }, body: ListView.builder( itemBuilder: (context, index) { return ListTile( title: Container( color: Colors.amber.withOpacity(0.3), child: Text('Item $index'), ), ); }, ), ), ), ); } } class _SliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { @override Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { return Container( color: Colors.blue.withOpacity(0.75), child: Placeholder(), ); } @override double get maxExtent => 300; @override double get minExtent => 200; @override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true; }
It's all good; except, I need the "header" to be transparent, but having it translucent, causes the underneathed list-items to get revealed (as per the screenshot below).
So, how to "mask-out" the "list items" that are visible through the "translucent header"?
-
matehat over 3 yearsIf you want it to be "translucent", what do you expect to see through if not the list items?
-
goodUser over 3 years@matehat The "header" will contain some widgets; also, the whole thing will be "overlayed" on a background layer. However, the code is all good, except only I want the "list of items" to be "framed" in its own view (to not to be observable through the "header").
-
-
goodUser over 3 yearsThe reason to use the pinned
SliverPersistentHeader
is that it's very performant; if only the "sliver list of items" could've been somehow "framed" in its "territory", then it would also be performant. Your code however, does provide a correct solution, but the approach rebuilds theHeader
widget together with the "container" widget for the list, on thescroll
event; updating thecurrentHeight
on every frame while scrolling, and they collectively cause some performance bottleneck. Could you please optimize your solution for a better performance. I appreciate your efforts on this. -
Kherel over 3 yearswe trigger the setState method only in a small range of scroll offset, when we need to change the height of the header widget.
-
goodUser over 3 yearsThat's exactly where the bottleneck happens (the "rebuild")! That's why I would like to somehow "frame" the "list of sliver items" in their "territory"!
-
goodUser over 3 yearsThis is the best solution by far; intuitive and performant! Thank you very much.