Implementing Neumorphic design in flutter without a package
Published: 2022-04-30 17:56:43

In recent times, few concepts have rocked the UI/UX design world in a fashion similar to the neumorphic design craze. [Dribbble](https://dribbble.com/) and [behance](https://www.behance.net/) are littered with designs illustrating the use of this design pattern in web or mobile apps. It's a minimal design thus very appealing to the eye due to its softness and uniqueness in a design landscape where popping, shouting colors are all the rave.
Luckily, we can implement these great designs in our flutter apps and make them stand out from the crowd(of apps*). I shall implement both a dark and light neumorphic design. For this demonstration, I shall use a simple fintech app UI sample I created. 
Here is the [source code](https://github.com/Raicodesokwe/kudidemo)
**Firstly**,we shall begin with the light version. We shall implement a base theme widget that shall hold all global theme properties that shall apply throughout the app. We shall use a scaffold background color of Color.grey[300] to enable use apply neumorphic shadows that pop effectively.

```
ThemeData lightThemeData(BuildContext context) {
  return ThemeData.light().copyWith(
      scaffoldBackgroundColor: Colors.grey[300],
}
```
Next, we shall create a box decoration variable that we shall use throughout the app to create a light box-shadow that shall create the popping effect present in neumorphic designs

```
final BoxDecoration decorator = BoxDecoration(
      [
              BoxShadow(
                  color: Colors.grey.shade500,
                  offset: Offset(4, 4),
                  blurRadius: 15,
                  spreadRadius: 1),
              BoxShadow(
                spreadRadius: 1,
                color: Colors.white,
                offset: Offset(-4, -4),
                blurRadius: 15,
              )
            ]);
```
If we want to add the neumorphic shadow to any widget that we use for example, container, we'll just add the decorator variable to the decoration property. 

```
 Container(
              height: size.height * 0.07,
              width: size.width * 0.4,
              decoration: decorator.copyWith(
                  borderRadius: BorderRadius.circular(30.0),
                  color: Colors.greenAccent),)
```
In certain instances, we might want to add a depression effect in our neumorphic design language so as to create an illusion of a depression on the screen as illustrated in the image below. This can also be created with flutter ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/al44vo665g6q6wy0v98v.jpg)
We shall create a decoration and box painter widget and add the colors we shall use to create the concave decoration effect

```
class SecondDecoration extends Decoration {
  final ShapeBorder? shape;
  final double? depression;
  final List<Color>? colors;

  SecondDecoration({
    @required this.shape,
    @required this.depression,
    this.colors,
  })  : assert(shape != null),
        assert(depression! >= 0),
        assert(colors == null || colors.length == 2);

  @override
  BoxPainter createBoxPainter([void onChanged]) =>
      _SecondDecorationPainter(shape!, depression!, colors!);

  @override
  EdgeInsetsGeometry get padding => shape!.dimensions;
}

class _SecondDecorationPainter extends BoxPainter {
  ShapeBorder shape;
  double depression;
  List<Color> colors;

  _SecondDecorationPainter(this.shape, this.depression, this.colors) {
    colors = [
      // Colors.black,
      // Colors.grey.shade800,
        Colors.black87,
       Colors.white
    ];
  }

  @override
  void paint(
      ui.Canvas canvas, ui.Offset offset, ImageConfiguration configuration) {
    final rect = offset & configuration.size!;
    final shapePath = shape.getOuterPath(rect);

    final delta = 16 / rect.longestSide;
    final stops = [0.5 - delta, 0.5 + delta];

    final path = Path()
      ..fillType = PathFillType.evenOdd
      ..addRect(rect.inflate(depression * 2))
      ..addPath(shapePath, Offset.zero);
    canvas.save();
    canvas.clipPath(shapePath);

    final paint = Paint()
      ..maskFilter = MaskFilter.blur(BlurStyle.normal, depression);
    final clipSize = rect.size.aspectRatio > 1
        ? Size(rect.width, rect.height / 2)
        : Size(rect.width / 2, rect.height);
    for (final alignment in [Alignment.topLeft, Alignment.bottomRight]) {
      final shaderRect =
          alignment.inscribe(Size.square(rect.longestSide), rect);
      paint.shader = ui.Gradient.linear(
          shaderRect.topLeft, shaderRect.bottomRight, colors, stops);

      canvas.save();
      canvas.clipRect(alignment.inscribe(clipSize, rect));
      canvas.drawPath(path, paint);
      canvas.restore();
    }
    canvas.restore();
  }
}
```
 We shall then pass this Decoration widget as an argument to the decoration property of a container widget.We shall then pass a pair of colors that shall give us a concave depression decoration effect in our container

```
Container(
                height: 60,
                decoration: SecondDecoration(
                        shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(10)),
                        depression: 10,
                        colors: [
                            Color.fromRGBO(216, 213, 208, 1),
                            Colors.white
                          ]))
```
And voila. We can now create a light neumorphic shadow and depression effect in our flutter apps.
Next up, we shall replicate the same in dark mode.
**Firstly**,we shall add a global dark theme with a dark scaffold color that shall enable us to create a dark neumorphic design.

```
ThemeData darkThemeData(BuildContext context) {
  return ThemeData.dark().copyWith(
      scaffoldBackgroundColor:Colors.grey[900],
      );
}
```
We shall then create a dark neumorphic shadow that shall create a popping effect. This shall be done by creating a decorator variable that shall hold the BoxDecoration object with the necessary box-shadow

```
final BoxDecoration decorator = BoxDecoration(
      [
              BoxShadow(
                  color: Colors.black,
                  offset: Offset(5, 5),
                  blurRadius: 15,
                  spreadRadius: 5),
              BoxShadow(
                spreadRadius: 1,
                color: Colors.grey.shade800,
                offset: Offset(-4, -4),
                blurRadius: 15,
              )
            ]);
```
We can now use this variable within the container widget down the widget tree.

```
Container(
              height: size.height * 0.07,
              width: size.width * 0.4,
              decoration: decorator.copyWith(
                  borderRadius: BorderRadius.circular(30.0),
                  color: Colors.greenAccent),)
```
We shall then create a dark concave depression neumorphic effect with Decoration and box painter widgets.

```
class ConcaveDecoration extends Decoration {
  final ShapeBorder? shape;
  final double? depression;
  final List<Color>? colors;

  ConcaveDecoration({
    @required this.shape,
    @required this.depression,
    this.colors,
  })  : assert(shape != null),
        assert(depression! >= 0),
        assert(colors == null || colors.length == 2);

  @override
  BoxPainter createBoxPainter([void onChanged]) =>
      _ConcaveDecorationPainter(shape!, depression!, colors!);

  @override
  EdgeInsetsGeometry get padding => shape!.dimensions;
}

class _ConcaveDecorationPainter extends BoxPainter {
  ShapeBorder shape;
  double depression;
  List<Color> colors;

  _ConcaveDecorationPainter(this.shape, this.depression, this.colors) {
    colors = [
      Colors.black,
      Colors.grey.shade800,
      //   Colors.black87,
      //  Colors.white
    ];
  }

  @override
  void paint(
      ui.Canvas canvas, ui.Offset offset, ImageConfiguration configuration) {
    final rect = offset & configuration.size!;
    final shapePath = shape.getOuterPath(rect);

    final delta = 16 / rect.longestSide;
    final stops = [0.5 - delta, 0.5 + delta];

    final path = Path()
      ..fillType = PathFillType.evenOdd
      ..addRect(rect.inflate(depression * 2))
      ..addPath(shapePath, Offset.zero);
    canvas.save();
    canvas.clipPath(shapePath);

    final paint = Paint()
      ..maskFilter = MaskFilter.blur(BlurStyle.normal, depression);
    final clipSize = rect.size.aspectRatio > 1
        ? Size(rect.width, rect.height / 2)
        : Size(rect.width / 2, rect.height);
    for (final alignment in [Alignment.topLeft, Alignment.bottomRight]) {
      final shaderRect =
          alignment.inscribe(Size.square(rect.longestSide), rect);
      paint.shader = ui.Gradient.linear(
          shaderRect.topLeft, shaderRect.bottomRight, colors, stops);

      canvas.save();
      canvas.clipRect(alignment.inscribe(clipSize, rect));
      canvas.drawPath(path, paint);
      canvas.restore();
    }
    canvas.restore();
  }
}
```
Here is how it appears ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/10d96fg2566bk2e8xf9x.jpg)
We can now use this concave decoration effect in container widgets within our widget tree

```
Container(
                height: 60,
                decoration:ConcaveDecoration(
                        colors: [Colors.black, Colors.grey.shade800],
                        shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(10)),
                        depression: 10))
```
The result is a great looking dark mode neumorphic design for our apps.
Here is a [video](https://twitter.com/muraideveloper/status/1520449533083275265) illustrating the feel and look of the sample app.
In the app, I also implement functionality enabling toggling between light mode and dark mode. I offered an explanation on how to implement that functionality [here](https://dev.to/raicodesokwe/implementing-persistent-dark-mode-in-flutter-4854)
Thanks for reading until the end and stay tuned for more flutter tips in future.