Skip to content

Instantly share code, notes, and snippets.

@coman3
Last active January 13, 2025 17:00
Show Gist options
  • Select an option

  • Save coman3/e631fd55cd9cdf9bd4efe8ecfdbb85a7 to your computer and use it in GitHub Desktop.

Select an option

Save coman3/e631fd55cd9cdf9bd4efe8ecfdbb85a7 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
@immutable
class ClipShadowPath extends StatelessWidget {
final Shadow shadow;
final CustomClipper<Path> 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<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;
}
}
@coman3

coman3 commented Jan 27, 2019

Copy link
Copy Markdown
Author

Usage:

  Widget buildBodyWidget(BuildContext context) {
    return ClipShadowPath(
      clipper: _MyCustomClipper(),
      shadow: Shadow(
        blurRadius: 5
      ),
      child: Scaffold(
        body: Center(child: Text("Test"),),
      ),
    );
  }

@patniemeyer

Copy link
Copy Markdown

Thanks for the example. You can also add the shadow offset by shifting the path:

var clipPath = clipper.getClip(size).shift(shadow.offset);
canvas.drawPath(clipPath, paint);

@coman3

coman3 commented Apr 9, 2019

Copy link
Copy Markdown
Author

Whoops, thanks for the heads up, forgot shadow has an offset! (i have updated the example)

@yahyahoush

Copy link
Copy Markdown

Hi
How can i add border Radius to the ClipRRect that is inside ClipShadowPath
i mean can i set Border Radius in the ClipShadowPath in any way using your method?
and thx

@tigrenok00

Copy link
Copy Markdown

Thank you!

@thiagocarvalhodev

Copy link
Copy Markdown

You are a great man. Thanks!!!!!!!!!!

@hardik-satasiya

Copy link
Copy Markdown

is PhysicalShape simple alternative ?

@happyharis

Copy link
Copy Markdown

Great solution. How about changing the Shadow object to BoxShadow object? The difference is that the Shadow object is missing a spreadRadius from BoxShadow.

@Alijaaan

Copy link
Copy Markdown

It was Great coman3
we can add opacity to shadow too :

var opacity = 0.6; paint.color = Color.fromRGBO(0, 0, 0, opacity);

@hahouari

hahouari commented May 1, 2020

Copy link
Copy Markdown

This is a masterpiece that made my work really shine, thanks.
However I have a suggestion, when developing with hot reload and making changes on the clipper inside getClip(), the UI does not update new values automatically , I suggest you add key: UniqueKey(), inside return CustomPaint(...), this way every change inside the getClip() is updated when hot reloaded.

@coman3

coman3 commented May 27, 2020

Copy link
Copy Markdown
Author

@dnory0, good thinking. Ive added this to the example :)

@hardik-satasiya, PhysicalShape does not support custom shadows and can only provide an 'elevation' which for my use case was not enough as i needed specific radii.

@happyharis, I was hesitant to force the usage of a BoxShadow, you should still be able to use it though as Shadow is the base class. See: https://master-api.flutter.dev/flutter/painting/BoxShadow-class.html

@hardik-satasiya

Copy link
Copy Markdown

@coman3 yes I understand it once i used it :)

thanks for your great solution :)

@mahmoudalaa97

Copy link
Copy Markdown

Thank you for this code πŸ‘ ❀️

@shelarsuhas

Copy link
Copy Markdown

This is awesome stuff @coman3. Really appreciate it. πŸ₯‡

@kallaspriit

Copy link
Copy Markdown

This is a masterpiece that made my work really shine, thanks.
However I have a suggestion, when developing with hot reload and making changes on the clipper inside getClip(), the UI does not update new values automatically , I suggest you add key: UniqueKey(), inside return CustomPaint(...), this way every change inside the getClip() is updated when hot reloaded.

Note that using key: UniqueKey() broke my AnimatedOpacity (and likely other animations) that was used as a child of ClipShadowPath.

@coman3

coman3 commented May 3, 2021

Copy link
Copy Markdown
Author

Note that using key: UniqueKey() broke my AnimatedOpacity (and likely other animations) that was used as a child of ClipShadowPath.

Thanks for the tip, I'm sure someone was fighting with this in the past since that change πŸ‘

@Lee-BS-AMS

Copy link
Copy Markdown

Interesting, what would _MyCustomClipper be? I'm looking to add a shadow to a ClipRRect

@sameerkhan24

Copy link
Copy Markdown

How would I add the color property here? I want to change the color of the shadow

@mvarendorff

Copy link
Copy Markdown

@sameerkhan24 You can just set the color on the shadow you pass to the ClipShadowPath (using the example provided by OP):

  Widget buildBodyWidget(BuildContext context) {
    return ClipShadowPath(
      clipper: _MyCustomClipper(),
      shadow: Shadow(
        blurRadius: 5,
        color: Colors.red, // This line was added
      ),
      child: Scaffold(
        body: Center(child: Text("Test"),),
      ),
    );
  }

@vmwsree

vmwsree commented Aug 29, 2021

Copy link
Copy Markdown

Can this be extended to add only a border?

@madhubansahani

Copy link
Copy Markdown

@geisterfurz007 please share with me the code for achieving
Shadow on the _MyCustomClipper

@mvarendorff

Copy link
Copy Markdown

@madhubansahani I am unsure what you are missing. _MyCustomClipper is a class that extends from CustomClipper, the rest are Flutter built-ins. shadow defines the look of the shadow you want and the child is the child you want to apply your custom shadow to.

@CristinaCismas

CristinaCismas commented Jan 31, 2023

Copy link
Copy Markdown

@coman3 Have you added any license for this snippet of code? Thank you :)

@coman3

coman3 commented Feb 2, 2023

Copy link
Copy Markdown
Author

@coman3 Have you added any license for this snippet of code? Thank you :)

Feel free to use this under the MIT license. ☺

@vincent-hoodoo

Copy link
Copy Markdown

@coman3 I've just been fighting a bug in my application, where the child of my ClipShadowPath is a stateful widget, but it was loosing its state every time its parent's build method was called. After an hour fighting it, I finally understood that it was caused by the CustomPaint widget defining its key as a UniqueKey().

Someone already posted a similar situation before, and as you noted yourself, there are probably others this happens to:

Note that using key: UniqueKey() broke my AnimatedOpacity (and likely other animations) that was used as a child of ClipShadowPath.

Thanks for the tip, I'm sure someone was fighting with this in the past since that change πŸ‘

Could you remove the key definition from your gist? I'm not sure what benefit it brings really, but it certainly causes many other developers to loose time figuring out why their application is buggy.

@coman3

coman3 commented Feb 15, 2023

Copy link
Copy Markdown
Author

@coman3 I've just been fighting a bug in my application, where the child of my ClipShadowPath is a stateful widget, but it was loosing its state every time its parent's build method was called. After an hour fighting it, I finally understood that it was caused by the CustomPaint widget defining its key as a UniqueKey().

Someone already posted a similar situation before, and as you noted yourself, there are probably others this happens to:

Note that using key: UniqueKey() broke my AnimatedOpacity (and likely other animations) that was used as a child of ClipShadowPath.

Thanks for the tip, I'm sure someone was fighting with this in the past since that change πŸ‘

Could you remove the key definition from your gist? I'm not sure what benefit it brings really, but it certainly causes many other developers to loose time figuring out why their application is buggy.

Sorry if this wasted ya time, I've removed it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment