fooder-app/lib/components/sliver.dart

172 lines
3.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:blur/blur.dart';
class ClipShadowPath extends StatelessWidget {
final Shadow shadow;
final CustomClipper<Path> clipper;
final Widget child;
const ClipShadowPath({
super.key,
required this.shadow,
required this.clipper,
required this.child,
});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _ClipShadowShadowPainter(
clipper: clipper,
shadow: shadow,
),
child: ClipPath(clipper: clipper, child: child),
);
}
}
class _ClipShadowShadowPainter extends CustomPainter {
final Shadow shadow;
final CustomClipper<Path> 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({super.key, required this.height});
@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<Path> {
@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,
left: 16,
right: 16,
child: child,
)
],
);
}
@override
double get maxExtent => 280;
@override
double get minExtent => 140;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
oldDelegate.maxExtent != maxExtent || oldDelegate.minExtent != minExtent;
}