Skip to content

Instantly share code, notes, and snippets.

@herveGuigoz
Last active April 21, 2025 09:21
Show Gist options
  • Save herveGuigoz/7ac8b5d6a4970aab9f6be53353379bea to your computer and use it in GitHub Desktop.
Save herveGuigoz/7ac8b5d6a4970aab9f6be53353379bea to your computer and use it in GitHub Desktop.
Flutter Sliver Header
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