Last active
April 21, 2025 09:21
-
-
Save herveGuigoz/7ac8b5d6a4970aab9f6be53353379bea to your computer and use it in GitHub Desktop.
Flutter Sliver Header
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:math' as math; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
class SliverHeader extends SliverPersistentHeaderDelegate { | |
SliverHeader({ | |
this.minExtent = 130, | |
this.leading, | |
this.middle, | |
this.trailing, | |
this.bottom, | |
this.automaticallyImplyLeading = true, | |
}); | |
final Widget? leading; | |
final Widget? middle; | |
final Widget? trailing; | |
final PreferredSizeWidget? bottom; | |
final bool automaticallyImplyLeading; | |
@override | |
final double minExtent; | |
@override | |
double get maxExtent => minExtent + (bottom?.preferredSize.height ?? 0); | |
@override | |
Widget build( | |
BuildContext context, | |
double shrinkOffset, | |
bool overlapsContent, | |
) { | |
final theme = Theme.of(context); | |
final topPadding = MediaQuery.of(context).padding.top; | |
final fixedSpace = minExtent + topPadding; | |
final currentExtent = math.max(fixedSpace, maxExtent - shrinkOffset); | |
final parentRoute = ModalRoute.of(context); | |
final canPop = parentRoute?.canPop ?? false; | |
Widget? leading = this.leading; | |
if (leading == null && automaticallyImplyLeading) { | |
if (canPop || (parentRoute?.impliesAppBarDismissal ?? false)) { | |
leading = const BackButton(); | |
} | |
} | |
leading = ConstrainedBox( | |
constraints: BoxConstraints.tightFor(width: minExtent - topPadding), | |
child: Center(child: leading), | |
); | |
final trailing = ConstrainedBox( | |
constraints: BoxConstraints.tightFor(width: minExtent - topPadding), | |
child: Center(child: this.trailing), | |
); | |
final middle = ConstrainedBox( | |
constraints: const BoxConstraints.tightFor(height: 40), | |
child: Center(child: this.middle), | |
); | |
final toolbar = IconTheme.merge( | |
data: theme.primaryIconTheme, | |
child: NavigationToolbar( | |
leading: leading, | |
middle: middle, | |
trailing: trailing, | |
), | |
); | |
return MediaQuery.removePadding( | |
context: context, | |
removeBottom: true, | |
child: AnnotatedRegion<SystemUiOverlayStyle>( | |
value: theme.appBarTheme.systemOverlayStyle!, | |
child: Material( | |
type: MaterialType.transparency, | |
child: Container( | |
alignment: Alignment.topCenter, | |
constraints: BoxConstraints.tightFor( | |
height: math.max(minExtent, currentExtent - 50), | |
), | |
decoration: const BoxDecoration( | |
borderRadius: BorderRadius.vertical( | |
bottom: Radius.circular(32), | |
), | |
gradient: LinearGradient( | |
begin: Alignment.topLeft, | |
end: Alignment.bottomRight, | |
colors: [Color(0xFFC80C0F), Color(0XFFF20A0E)], | |
stops: [.21, 1], | |
), | |
), | |
child: SafeArea( | |
child: toolbar, | |
), | |
), | |
), | |
), | |
); | |
} | |
@override | |
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { | |
return bottom != (oldDelegate as SliverHeader).bottom; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment