import 'package:flutter/material.dart'; import 'package:blur/blur.dart'; class ClipShadowPath extends StatelessWidget { final Shadow shadow; final CustomClipper clipper; final Widget child; ClipShadowPath({ required this.shadow, required this.clipper, required this.child, }); @override Widget build(BuildContext context) { return CustomPaint( painter: _ClipShadowShadowPainter( clipper: this.clipper, shadow: this.shadow, ), child: ClipPath(child: child, clipper: this.clipper), ); } } class _ClipShadowShadowPainter extends CustomPainter { final Shadow shadow; final CustomClipper clipper; _ClipShadowShadowPainter({required this.shadow, required this.clipper}); @override void paint(Canvas canvas, Size size) { var paint = shadow.toPaint(); var clipPath = clipper.getClip(size).shift(shadow.offset); canvas.drawPath(clipPath, paint); } @override bool shouldRepaint(CustomPainter oldDelegate) { return true; } } class BackgroundWave extends StatelessWidget { final double height; const BackgroundWave({Key? key, required this.height}) : super(key: key); @override Widget build(BuildContext context) { var theme = Theme.of(context); var colorScheme = theme.colorScheme; return SizedBox( height: height, child: ClipShadowPath( clipper: BackgroundWaveClipper(), shadow: BoxShadow( blurRadius: 5, color: colorScheme.primary.withOpacity(0.3), offset: const Offset(0, 5), ), child: Blur( blur: 10, blurColor: colorScheme.primary.withOpacity(0.1), child: Container( width: MediaQuery.of(context).size.width, height: height, decoration: BoxDecoration( gradient: LinearGradient( colors: [ colorScheme.primary.withOpacity(0.1), colorScheme.secondary.withOpacity(0.1), ], ), ), ), ), ), ); } } class BackgroundWaveClipper extends CustomClipper { @override Path getClip(Size size) { var path = Path(); path.lineTo(0.0, size.height); var firstCurve = Offset(0, size.height - 20); var lastCurve = Offset(40, size.height - 20); path.quadraticBezierTo( firstCurve.dx, firstCurve.dy, lastCurve.dx, lastCurve.dy, ); firstCurve = Offset(0, size.height - 20); lastCurve = Offset(size.width - 40, size.height - 20); path.quadraticBezierTo( firstCurve.dx, firstCurve.dy, lastCurve.dx, lastCurve.dy, ); firstCurve = Offset(size.width, size.height - 20); lastCurve = Offset(size.width, size.height); path.quadraticBezierTo( firstCurve.dx, firstCurve.dy, lastCurve.dx, lastCurve.dy, ); path.lineTo(size.width, 0.0); path.close(); return path; } @override bool shouldReclip(BackgroundWaveClipper oldClipper) => oldClipper != this; } class FSliverAppBar extends SliverPersistentHeaderDelegate { final Widget child; const FSliverAppBar({required this.child}); @override Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { var adjustedShrinkOffset = shrinkOffset > minExtent ? minExtent : shrinkOffset; double offset = (minExtent - adjustedShrinkOffset); if (offset < 4) { offset = 4; } return Stack( children: [ const BackgroundWave( height: 280, ), Positioned( top: offset, child: child, left: 16, right: 16, ) ], ); } @override double get maxExtent => 280; @override double get minExtent => 140; @override bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => oldDelegate.maxExtent != maxExtent || oldDelegate.minExtent != minExtent; }