How to apply glass-like 3D effect on a button on Flutter?

 


It is easy to use image as button resource. If you want the 3D shadow effect by code, it can be achieved by 
shadows inside Container widget.

First: build the shape of the button

Here is the shape path

Path myCustomShapePath(Rect rect) {
  var r1 = rect.height * 0.5;
  var r2 = rect.height * 0.35;
  var r3 = rect.height * 0.1;
  var L = rect.left;
  var R = rect.right;
  var T = rect.top;
  var B = rect.bottom;

  return Path()
    ..moveTo(L, T + r2)
    ..arcTo(Rect.fromLTWH(L, T, r2, r2), pi, 0.5 * pi, false)
    ..lineTo(R - r2, T)
    ..arcTo(Rect.fromLTWH(R - r2, T, r2, r2), 1.5 * pi, 0.5 * pi, false)
    ..lineTo(R, B - r1 - 2 * r3)
    ..arcTo(Rect.fromLTWH(R - r3, B - r1 - 2 * r3, r3, r3), 0, 0.5 * pi, false)
    ..arcTo(Rect.fromLTWH(R - r1 - r3, B - r1 - r3, r1 * 2.5, r1 * 2.5),
        1.5 * pi, -0.5 * pi, false)
    ..arcTo(Rect.fromLTWH(R - r1 - 2 * r3, B - r3, r3, r3), 0, 0.5 * pi, false)
    ..lineTo(R - r2, B)
    ..arcTo(Rect.fromLTWH(L, B - r2, r2, r2), 0.5 * pi, 0.5 * pi, false)
    ..close();
}

Because I want the custom shape for the button and avoid the shadow blur outside of the button, so I extend both ShapeBorder and CustomClipper<Path> with the same path:

CustomClipPath

class CustomClipPath extends CustomClipper<Path> {
  @override
  Path getClip(Size size) =>
      myCustomShapePath(Rect.fromLTRB(0, 0, size.width, size.height));
  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}

CustomShape

class CustomShape extends ShapeBorder {
  @override
  EdgeInsetsGeometry get dimensions => const EdgeInsets.only();

  @override
  Path getInnerPath(Rect rect, {TextDirection textDirection}) =>
      getOuterPath(rect, textDirection: textDirection);

  @override
  Path getOuterPath(Rect rect, {TextDirection textDirection}) =>
      myCustomShapePath(rect);
  @override
  void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}

  @override
  ShapeBorder scale(double t) => null;
}

Second: use BoxShadow to draw the button

Use ClipPath to clip the shadow blur and use shadows inside Container's ShapeDecoration to draw the 3D effect:

class MySolidButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ClipPath(
      clipper: CustomClipPath(),
      child: Container(
        width: 200,
        height: 100,
        decoration: ShapeDecoration(
          shape: CustomShape(),
          shadows: [
            BoxShadow(color: Colors.white),
            BoxShadow(
              color: Color(0xFF550091),
              offset: Offset(0, 20),
              blurRadius: 5,
              spreadRadius: 10,
            ),
            BoxShadow(
              color: Color(0xFFA93EF0),
              blurRadius: 10,
              spreadRadius: -2,
            ),
          ],
        ),
        child: FlatButton(
          onPressed: () {},
        ),
      ),
    );
  }
}

Result

enter image description here


Update:

BoxShadow 1BoxShadow 2BoxShadow 3Combine (Before Clip)
color: Colors.whitecolor: Color(0xFF550091)
blurRadius: 5
spreadRadius: 10
color: Color(0xFFA93EF0)
blurRadius: 10
spreadRadius: -2
(shadow 2 move down)
offset: Offset(0, 20)
shadow 1shadow 2shadow 3enter image description here

LayoutBuilder

  child: Container(
    width: 300,
    height: 200,
    child: LayoutBuilder(
      builder: (context, constraint) {
        // get constraint here 300 x 200
        return Container(
          decoration: ShapeDecoration(
            shape: CustomShape(),

Post a Comment

Previous Post Next Post

Subscribe Us


Get tutorials, Flutter news and other exclusive content delivered to your inbox. Join 1000+ growth-oriented Flutter developers subscribed to the newsletter

100% value, 0% spam. Unsubscribe anytime