Every once in a while you want to draw the attention of a user to a specific part of your app. One way of doing so is blurring all screen content, except a small cutout. To take it up a notch, also darken the blurred area.

The general idea how to achieve this in flutter is

  1. Create a stack with two items
    1. The app content
    2. A BackdropFilter using ImageFilter.blur
  2. Create a cutout using CustomPaint

But let’s take it slow and go step by step.

Blur content using a stack and a backdrop filter

This is pretty straight forward. Wrap your existing content in a stack with 2 items: your content and a BackdropFilter. The child of the BackdropFilter decides on the region the filter is applied. The example below uses a container that takes up all available space.

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Stack(
        children: [
            YourHomeScreenContent(),
            // This is where the blurring happens
            BackdropFilter(
                filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
                // A container that expands to the full available space.
                child: Container(
                    alignment: Alignment.center,
                    color: Colors.transparent,
                ),
            ),
        ],
    );
  }
}

Create a cutout to the blur area

To create a cutout, add a CustomPaint child to the container defining the blur area.

          BackdropFilter(
            filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
            child: Container(
              alignment: Alignment.center,
              // Choose Colors.black.withOpacity(0.3) here if you want a shadow effect in addition to blurring.
              color: Colors.transparent,
              // This part is new, creating the cutout.
              child: CustomPaint(
                  size: size,
                  painter: Hole(),
              ),
            ),
          ),

Here’s an example implementation for creating a circular hole. CustomPainter also supports much more advanced shapes (and combination of shapes!) so make sure to read up on the documentation.

/// [Hole] provides a custom painter for leaving a circular hole with some
/// fuziness.
class Hole extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    double radius = 100;
    double blurRadius = 10;
    canvas.drawCircle(
      Offset(0, -100),
      radius,
      Paint()
        ..blendMode = BlendMode.xor
        // The mask filter gives some fuziness to the cutout.
        ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurRadius),
    );
  }

  @override
  bool shouldRepaint(Hole oldDelegate) => false;
}