Last active
February 1, 2025 15:25
-
-
Save orihpt/58546ff5961838021fa548a97f61978c to your computer and use it in GitHub Desktop.
Inner Shadow in Flutter that actually works
This file contains 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
// An inner shadow that fucking works. Thanks Flutter team "developers" and internet losers for wasting my time. | |
import 'dart:ui'; | |
import 'package:flutter/material.dart'; | |
class InnerShadow extends StatelessWidget { | |
final Widget child; | |
final double blurRadius; | |
final Radius radius; | |
final Offset offset; | |
final Color color; | |
final double opacity; | |
const InnerShadow({ | |
required this.child, | |
this.blurRadius = 10, | |
this.radius = const Radius.circular(0), | |
this.offset = const Offset(0, 0), | |
this.color = Colors.black, | |
this.opacity = 1, | |
super.key, | |
}); | |
@override | |
Widget build(BuildContext context) { | |
return Stack( | |
children: [ | |
child, | |
Positioned.fill( | |
child: ClipRRect( | |
borderRadius: BorderRadius.all(radius), | |
clipBehavior: Clip.antiAlias, | |
child: LayoutBuilder( | |
builder: (context, constraints) { | |
final outerWidth = constraints.maxWidth * 2; | |
final outerHeight = constraints.maxHeight * 2; | |
final width = constraints.maxWidth; | |
final height = constraints.maxHeight; | |
return OverflowBox( | |
maxWidth: outerWidth, | |
minWidth: outerWidth, | |
maxHeight: outerHeight, | |
minHeight: outerHeight, | |
child: Transform.translate( | |
offset: offset, | |
child: ImageFiltered( | |
imageFilter: ImageFilter.blur(sigmaX: blurRadius, sigmaY: blurRadius), | |
child: SizedBox( | |
width: outerWidth, | |
height: outerHeight, | |
child: ClipPath( | |
clipper: HoleClipper(radius: radius, size: constraints.biggest), | |
child: Container( | |
width: width, | |
height: height, | |
color: color.withAlpha((opacity * 255).toInt()), | |
), | |
), | |
), | |
), | |
), | |
); | |
}, | |
), | |
), | |
), | |
], | |
); | |
} | |
} | |
class HoleClipper extends CustomClipper<Path> { | |
final Radius radius; | |
final Size size; | |
HoleClipper({this.radius = Radius.zero, required this.size}); | |
@override | |
Path getClip(Size size) { | |
Path path = Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height)); | |
Rect hole = Rect.fromCenter( | |
center: Offset(size.width / 2, size.height / 2), | |
width: this.size.width, | |
height: this.size.height, | |
); | |
path.addRRect(RRect.fromRectAndRadius(hole, radius)); | |
return Path.combine(PathOperation.difference, path, Path()..addRRect(RRect.fromRectAndRadius(hole, radius))); | |
} | |
@override | |
bool shouldReclip(HoleClipper oldClipper) => oldClipper.radius != radius || oldClipper.size != size; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment