How to clip the BackdropFilter with smooth edges?

1,526

Solution 1

I was able to achieve it but it is kind of way out as there is no way to directly implement it as of now. Make this function for creating the effect. This function will recieve a list of widgets that you want to blur.

List<Widget> buildBlurredImage(List<Widget> l) {
    List<Widget> list = [];
    list.addAll(l);
    double sigmaX = 0;
    double sigmaY = 0.1;
    for (int i = 100; i < 350; i += 5) {
     // 100 is the starting height of blur
     // 350 is the ending height of blur
      list.add(Positioned(
        top: i.toDouble(),
        bottom: 0,
        left: 0,
        right: 0,
        child: ClipRect(
          child: BackdropFilter(
            filter: ImageFilter.blur(
              sigmaX: sigmaX,
              sigmaY: sigmaY,
            ),
            child: Container(
              color: Colors.black.withOpacity(0),
            ),
          ),
        ),
      ));
      sigmaX += 0.1;
      sigmaY += 0.1;
    }
    return list;
  }

then inside your widget class inside stack use the function like this

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Stack(
          alignment: Alignment.bottomCenter,
          // children: <Widget>[],
          children: buildBlurredImage([
            Container(
              height: 500,
              child: Image.network(
                'https://www.thewowstyle.com/wp-content/uploads/2015/02/Beautiful-Wallpapers-14.jpg',
                fit: BoxFit.cover,
              ),
            ),
          ]),
        ),
      ),
    );
  }

enter image description here

Your final Widget class will look like this

class MyApp extends StatelessWidget {
  List<Widget> buildBlurredImage(List<Widget> l) {
    List<Widget> list = [];
    list.addAll(l);
    double sigmaX = 0;
    double sigmaY = 0.1;
    for (int i = 100; i < 350; i += 5) {
      // 100 is the starting height of blur
      // 350 is the ending height of blur
      list.add(Positioned(
        top: i.toDouble(),
        bottom: 0,
        left: 0,
        right: 0,
        child: ClipRect(
          child: BackdropFilter(
            filter: ImageFilter.blur(
              sigmaX: sigmaX,
              sigmaY: sigmaY,
            ),
            child: Container(
              color: Colors.black.withOpacity(0),
            ),
          ),
        ),
      ));
      sigmaX += 0.1;
      sigmaY += 0.1;
    }
    return list;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Stack(
          alignment: Alignment.bottomCenter,
          // children: <Widget>[],
          children: buildBlurredImage([
            Container(
              height: 500,
              child: Image.network(
                'https://www.thewowstyle.com/wp-content/uploads/2015/02/Beautiful-Wallpapers-14.jpg',
                fit: BoxFit.cover,
              ),
            ),
          ]),
        ),
      ),
    );
  }
}

Solution 2

The best approach for me would be to used a ShaderMask above the BackdropFilter.

Unfortunately that stop working due to a change in the engine: link

I created a issue on flutter, and I hope they solve the bug soon.

Share:
1,526
Aditya
Author by

Aditya

Updated on December 15, 2022

Comments

  • Aditya
    Aditya over 1 year

    I wanted to apply a BackdropFilter over an image in Flutter. So, I used the following way to apply the filter as given in the Flutter docs.

    import 'dart:ui';
    import 'package:flutter/material.dart';
    
    void main() {
       runApp(
        MaterialApp(
          debugShowCheckedModeBanner: false,
          home: Scaffold(
            body: MyApp(),
          ),
        ),
      );
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Stack(
              alignment: Alignment.bottomCenter,
              children: <Widget>[
                Container(
                  height: 500,
                  child: Image.network('https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQlXYdeEKhFq34sh8-0ZKC1uqCcVGgOzdW_ZRAqCBkWxG-oeCB1'),
                ),
                Positioned(
                  top: 300,
                  bottom: 0,
                  left: 0,
                  right: 0,
                  child: ClipRect(
                    child: BackdropFilter(
                      filter: ImageFilter.blur(sigmaX: 2, sigmaY: 10),
                      child: Container(
                        color: Colors.black.withOpacity(0),
                      ),
                    ),
                  ),
                ),
              ]
            ),
          ),
        );
      }
    }
    

    It produced the following output: Output of my code

    I am getting a hard edge between the BackDropFilter and the Image. Although, I want a smooth edge between them.

    How can I achieve something like this?