Flutter BackdropFilter 實現高斯模糊

愛小麗0427發表於2019-05-24

現在應該很多地方都會使用到高斯模糊的效果,想當初在Android上實現差點沒要了我的老命,那麼在 Flutter 中實現會是如何?

什麼是BackdropFilter,怎麼使用

Flutter 提供了 BackdropFilter 來實現高斯模糊的效果,照例開啟原始碼:

class BackdropFilter extends SingleChildRenderObjectWidget {
  /// Creates a backdrop filter.
  ///
  /// The [filter] argument must not be null.
  const BackdropFilter({
    Key key,
    @required this.filter,
    Widget child,
  }) : assert(filter != null),
       super(key: key, child: child);

  /// The image filter to apply to the existing painted content before painting the child.
  ///
  /// For example, consider using [ImageFilter.blur] to create a backdrop
  /// blur effect
  final ui.ImageFilter filter;
}
複製程式碼

可以看到必須要傳一個 ImageFilter filter ,而且註釋上也寫了

For example, consider using [ImageFilter.blur] to create a backdrop blur effect

例如,考慮使用 ImageFilter.blur 來說建立一個背景模糊的效果。

而且,在類的上面還有很長的一段註釋:

/// A widget that applies a filter to the existing painted content and then
/// paints [child].
///
/// The filter will be applied to all the area within its parent or ancestor
/// widget's clip. If there's no clip, the filter will be applied to the full
/// screen.
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=dYRs7Q1vfYI}
///
/// {@tool sample}
/// If the [BackdropFilter] needs to be applied to an area that exactly matches
/// its child, wraps the [BackdropFilter] with a clip widget that clips exactly
/// to that child.
///
/// ```dart
/// Stack(
///   fit: StackFit.expand,
///   children: <Widget>[
///     Text('0' * 10000),
///     Center(
///       child: ClipRect(  // <-- clips to the 200x200 [Container] below
///         child: BackdropFilter(
///           filter: ui.ImageFilter.blur(
///             sigmaX: 5.0,
///             sigmaY: 5.0,
///           ),
///           child: Container(
///             alignment: Alignment.center,
///             width: 200.0,
///             height: 200.0,
///             child: Text('Hello World'),
///           ),
///         ),
///       ),
///     ),
///   ],
/// )
/// ```
複製程式碼

最前面一段話說的是:如果你不設定他大小的話,這個元件將是全屏的。

然後!他放出來了一個 YouTube 的視訊!還有一段Demo

那話不多說,我們直接執行官方給出來的Demo,看看是什麼效果:

Flutter BackdropFilter 實現高斯模糊

當這一大串 0 顯示在我眼前的時候我差點瞎了。

不過可以看到使用 BackdropFilter 非常簡單就實現了高斯模糊的效果。

自定義高斯模糊元件

那我們可以按照Demo的思路來封裝幾個背景是高斯模糊的控制元件:

class BlurOvalWidget extends StatelessWidget {
  final Widget _widget;
  double _padding = 10;

  BlurOvalWidget(this._widget, {double padding = 0}) {
    if (padding != 0) this._padding = padding;
  }

  @override
  Widget build(BuildContext context) {
    return ClipOval(
      child: BackdropFilter(
        filter: ImageFilter.blur(
          sigmaX: 10,
          sigmaY: 10,
        ),
        child: Container(
          color: Colors.white10,
          padding: EdgeInsets.all(_padding),
          child: _widget,
        ),
      ),
    );
  }
}
複製程式碼

我們使用的是無狀態的小部件,建構函式裡需要傳入一個widget,用來放在模糊的背景上面。

然後我們的build 方法直接返回一個圓形的模糊背景,橫縱向模糊的數值為10,值越大,模糊的效果就越大。

不能光有圓形的模糊背景,再來一個圓角矩形的:

class BlurRectWidget extends StatelessWidget {
  final Widget _widget;
  double _padding = 10;

  BlurRectWidget(this._widget, {double padding = 0}) {
    if (padding != 0) this._padding = padding;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 50),
      child: ClipRRect(
        borderRadius: BorderRadius.all(Radius.circular(10)),
        child: BackdropFilter(
          filter: ImageFilter.blur(
            sigmaX: 20,
            sigmaY: 20,
          ),
          child: Container(
            color: Colors.white10,
            padding: EdgeInsets.all(_padding),
            child: _widget,
          ),
        ),
      ),
    );
  }
}
複製程式碼

程式碼基本一樣,只不過就是把 ClipOval 換成了 ClipRRect

現在我們用封裝好的兩個 widget 來實現一個比較簡單的頁面:

Flutter BackdropFilter 實現高斯模糊

上面的文字用到了我們定義的圓角矩形,下面的用到了我們定義的圓形。

程式碼如下,比較簡單,就是普通的搭建頁面:

class _BackdropFilterPageState extends State<BackdropFilterPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('BackdropFilterPageState'),
      ),
      body: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          Image.asset(
            'images/bg.jpg',
            fit: BoxFit.cover,
          ),
          Center(
            child: BlurRectWidget(
              Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  Text(
                    'BackdropFilter class',
                    style: TextStyle(
                      fontSize: 16,
                      color: Colors.white,
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.only(top: 5.0),
                    child: Text(
                      'A widget that applies a filter to the existing painted content and then paints child.'
                      'The filter will be applied to all the area within its parent or ancestor widget\'s clip. If there\'s no clip, the filter will be applied to the full screen.',
                      style: TextStyle(fontSize: 14, color: Colors.black87),
                      textAlign: TextAlign.justify,
                    ),
                  ),
                ],
              ),
            ),
          ),
          Container(
            alignment: Alignment.bottomCenter,
            margin: EdgeInsets.only(bottom: 150),
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                BlurOvalWidget(
                  IconButton(
                    onPressed: (){
                      Navigator.of(context).push(MaterialPageRoute(builder: (context){
                        return BlurImagePage();
                      }));
                    },
                    icon: Icon(
                      Icons.favorite,
                      color: Colors.white,
                    ),
                    iconSize: 30,
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 50.0),
                  child: BlurOvalWidget(
                    Icon(
                      Icons.share,
                      color: Colors.white,
                      size: 30,
                    ),
                  ),
                ),
                BlurOvalWidget(
                  Icon(
                    Icons.bookmark,
                    color: Colors.white,
                    size: 30,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
複製程式碼

如何把高斯模糊當做前景

那這個時候就有人問了,只能把模糊放在背景裡嗎,不能用來當前景嗎?

當然可以,不然我還費這麼多話幹什麼?

先看一下效果圖:

Flutter BackdropFilter 實現高斯模糊

怎麼樣,是不是你要的感覺?,有沒有一種想要充錢的衝動!

當然,這種效果實現起來也是非常的簡單,我們只需要把 BackdropFilter 的 child 設定為一個 Container(),並且設定上顏色(我這裡使用的是 Colors.white10),然後放在 Stack 中就ok啦。

程式碼如下:

class BlurImagePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
          alignment: Alignment.center,
          child: Stack(
            alignment: Alignment.center,
            children: <Widget>[
              Container(
                margin: EdgeInsets.symmetric(horizontal: 20),
                child: Image.asset(
                  'images/wanimal.png',
                  fit: BoxFit.cover,
                ),
              ),
              Positioned.fill(
                child: BackdropFilter(
                  filter: ImageFilter.blur(
                    sigmaX: 15,
                    sigmaY: 15,
                  ),
                  child: Container(
                    color: Colors.white10,
                  ),
                ),
              ),
              RaisedButton(
                textColor: Colors.white,
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
                color: Colors.orangeAccent,
                child: Text('充錢檢視更多', style: TextStyle(fontSize: 16),),
                onPressed: (){},
              )
            ],
          )),
    );
  }
}
複製程式碼

先放我們需要被遮住的 widget,然後放上我們的模糊 widget,再然後就可以讓使用者充錢了。

小結

BackdropFilter 不僅僅只可以做高斯模糊的效果,也可以用來做旋轉,傾斜等。

只不過我們常用的是高斯模糊,其原理一樣。

瞭解更多可以移步 Flutter 官網:flutter.dev/

原文程式碼移步Giuhub:github.com/wanglu1209/…

圖我就不傳了,23333.

Flutter BackdropFilter 實現高斯模糊

相關文章