Enhance your design of neumorphism in Flutter with inset shadows

Wenkai Fan
ITNEXT
Published in
3 min readMar 17, 2021

--

Neumorphism is one of the hottest design topics last year. It provides a soft and natural feeling to your UI. It is a careful selection of shadows, background color/gradient, and the surrounding environment. There have been multiple packages for achieving neumorphism in Flutter. The most famous one being flutter_neumorphic. If you just want to achieve the neumorphism effect, I strongly recommend this package. This package has the strength of being able to animate buttons that are “pressed” below the surface. Most of the previous articles/packages in Flutter do not support this as there is no built-in inset shadow like CSS does.

A neumorphic button animated from elevated to pressed

flutter_neumorphic achieved this inner shadow effect by extending the BoxPainter class and use a depth value to determine where the shadows should be. I would like to take the CSS approach and support inset shadows directly, as it will give your even more customization capabilities. If you now look at the morphable_shape package, there is a Widget called DecoratedShadowedShape that looks like the following:

Widget widget = DecoratedShadowedShape(
shape: shape,
shadows: shadows,
insetShadows: insetShadows,
decoration: decoration,
child: child
);

It will paint itself in the following order: shadows -> decoration -> insetShadows -> child -> border of the shape, and clip itself according to the shape parameter.

But this is not what I am going to use directly in this article. What I am actually using is the animated_styled_widget package. For an overview of this package, check out this medium post. It basically lets you define a Style instance similar to CSS and construct a responsive Widget. It also allows you to animate either implicitly or explicitly.

Here's what a neumorphic button looks like using this package:

And the relevant code looks like this:

beginStyle = Style(
width: 400.toPXLength,
height: 400.toPXLength,
padding: DynamicEdgeInsets.symmetric(vertical: 20.toPXLength),
backgroundDecoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.grey.shade50, Colors.grey.shade200])),
shapeBorder: RectangleShapeBorder(
borderRadius:
DynamicBorderRadius.all(DynamicRadius.circular(50.toPXLength)),
),
shadows: [
DynamicShapeShadow(
blurRadius: 20.toPXLength,
spreadRadius: -5.toPXLength,
color: Colors.grey.shade400,
offset: DynamicOffset(20.toPXLength, 20.toPXLength)),
DynamicShapeShadow(
blurRadius: 20.toPXLength,
spreadRadius: -5.toPXLength,
color: Color(0xFFFEFEFE),
offset: DynamicOffset(-20.toPXLength, -20.toPXLength)),
],
mouseCursor: SystemMouseCursors.click);
endStyle = beginStyle.copyWith(
backgroundDecoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.grey.shade50, Colors.grey.shade200])),
shadows:[],
insetShadows: [
DynamicShapeShadow(
blurRadius: 20.toPXLength,
spreadRadius: -5.toPXLength,
color: Colors.grey.shade400,
offset: DynamicOffset(20.toPXLength, 20.toPXLength)),
DynamicShapeShadow(
blurRadius: 20.toPXLength,
spreadRadius: -5.toPXLength,
color: Color(0xFFFEFEFE),
offset: DynamicOffset(-20.toPXLength, -20.toPXLength)),
]);
...@override
Widget build(BuildContext context) {
return AnimatedStyledContainer(
duration: Duration(milliseconds: 100),
style: toggleStyle ? beginStyle : endStyle,
child: Container());
}

You just define the beginStyle and the endStyle and the framework will automatically animate between them for you.

You can combine two neumorphic buttons together and coordinate their shadows.

In the above scenario, the light is coming from the top left corner. So if the larger button is unpressed, the smaller one will not have a lighter shadow at its top left corner.

Or you can nest two neumorphic buttons together and make the contrast even bigger. The side view of this button looks like this:

You can also change the shape of the button easily during the animation thanks to the morphable_shape package.

And that’s it for today. Inset shadows support and an easy way to create neumorphism designs in Flutter. Thank you!

--

--

Ph.D. in Nuclear Physics, M.S. in Computer Science at Duke University, Flutter lover