天天看點

Flutter 53: 圖解 BackdropFilter 高斯模糊

      小菜在學習時想要做一點類似毛玻璃的效果,首先想到的是高斯模糊,對于原生 Android 需要話費很多精力,而 Flutter 提供了 BackdropFilter 高斯模糊的 Widget,真的很友善;

Flutter 53: 圖解 BackdropFilter 高斯模糊

源碼分析

const BackdropFilter({
    Key key,
    @required this.filter,
    Widget child
})           

      分析源碼,必須要傳遞 filter 參數,用來構模組化糊層效果;小菜了解隻是通過 BackdropFilter 建構一個模糊圖層,借助 Stack 等設定圖層是在上層或下層,官方也推薦了 DecoratedBox 設定圖層上下;

      模糊圖層通常借用 ImageFilter.blur 設定模糊度,一般是在 0.0-10.0 之間,數值越大模糊度越高,超過 10.0 時完全不可見;

      小菜在設定模糊顔色時嘗試了 withOpacity 方法,一般是在 0.0-1.0 之間,用來設定顔色值的透明度;也可以采用 withAlpha 方法,效果相同,一般是在 0-255 之間;同時還可以采用 withRed / withGreen / withBlue 直接設定三原色的基礎值實作不同效果;

Widget _imageBackWid() {
  return Center(
      child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
    Row(children: <Widget>[
      _itemWid(Colors.red.withOpacity(0.1), 4.0),
      _itemWid(Colors.white.withOpacity(0.1), 8.0),
    ]),
    Row(children: <Widget>[
      _itemWid(Colors.white.withOpacity(0.1), 4.0),
      Container(
          width: MediaQuery.of(context).size.width * 0.5,
          height: MediaQuery.of(context).size.width * 0.5,
          child: BackdropFilter(
              filter: ImageFilter.blur(sigmaX: 4.0, sigmaY: 4.0),
              child: Container(
                  color: Colors.white.withOpacity(0.1),
                  child: Padding(
                      padding: EdgeInsets.all(14.0),
                      child: Image.asset('images/icon_hzw01.jpg')))))
    ])
  ]));
}

Widget _itemWid(color, blur) {
  return Container(
      width: MediaQuery.of(context).size.width * 0.5,
      height: MediaQuery.of(context).size.width * 0.5,
      child: Stack(children: <Widget>[
        Padding(
            padding: EdgeInsets.all(14.0),
            child: Image.asset('images/icon_hzw01.jpg')),
        BackdropFilter(
            filter: ImageFilter.blur(sigmaX: blur, sigmaY: blur),
            child: Container(color: color))
      ]));
}           
Flutter 53: 圖解 BackdropFilter 高斯模糊

      小菜嘗試了一個很不完善的小案例,類似于檢視截圖的小視窗,遮罩層是模糊圖層;小菜用了很蠢的方式來處理,底部是一個設定了高斯模糊的背景圖,上層通過 Canvas 展示一張完全相同的圖檔,借用 drawImage 實作小視窗,再通過手指坐标控制視窗位置;未對機型适配,僅作為一個不完善的小測試案例;

      小菜附上核心代碼僅供參考,若有更好的方法請多多指導!

class BackDropCanvas extends CustomPainter {
  ui.Image background;
  BuildContext context;
  var dx = 0.0, dy = 0.0;
  static const double smallScan = 200.0;
  BackDropCanvas(this.context, this.background, this.dx, this.dy);

  @override
  void paint(Canvas canvas, Size size) {
    canvas.clipPath(Path()
      ..moveTo((Screen.width - smallScan) * 0.5 + dx,
          (Screen.height - smallScan) * 0.5 + dy)
      ..lineTo((Screen.width + smallScan) * 0.5 + dx,
          (Screen.height - smallScan) * 0.5 + dy)
      ..lineTo((Screen.width + smallScan) * 0.5 + dx,
          (Screen.height + smallScan) * 0.5 + dy)
      ..lineTo((Screen.width - smallScan) * 0.5 + dx,
          (Screen.height + smallScan) * 0.5 + dy));
    canvas.drawImage(this.background, ui.Offset.zero, Paint());
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

class _BackedDropPage extends State<BackedDropPage> {
  ui.Image _background;
  bool _prepDone;
  var dx = 0.0, dy = 0.0;
  static const double smallScan = 200.0;

  @override
  void initState() {
    _prepDone = false;
    super.initState();
    _prepare();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Stack(children: <Widget>[
      Container(
          width: Screen.width,
          height: Screen.height,
          child: Stack(children: <Widget>[
            Center(child: Image.asset('images/icon_hzw04_1.jpg')),
            BackdropFilter(
                filter: ImageFilter.blur(sigmaX: 4.0, sigmaY: 4.0),
                child: Container(color: Colors.black.withOpacity(0.2)))
          ])),
      _prepDone
          ? Container(
              width: MediaQuery.of(context).size.width,
              height: MediaQuery.of(context).size.height,
              child: CustomPaint(
                  painter: BackDropCanvas(context, _background, dx, dy)))
          : Container(color: Colors.grey),
      GestureDetector(onPanUpdate: (d) {
        if (d.globalPosition.dx >= smallScan * 0.5 &&
            d.globalPosition.dx <= Screen.width - smallScan * 0.5) {
          dx = d.globalPosition.dx - Screen.width * 0.5;
        }
        if (d.globalPosition.dy >= smallScan * 0.5 &&
            d.globalPosition.dy <= Screen.height - smallScan * 0.5) {
          dy = d.globalPosition.dy - Screen.height * 0.5;
        }
        setState(() {});
      })
    ]));
  }

  _prepare() {
    loadImage('images/icon_hzw04_1.jpg').then((image) {
      _background = image;
    }).whenComplete(() {
      _prepDone = true;
      setState(() {});
    });
  }
}           
Flutter 53: 圖解 BackdropFilter 高斯模糊

      BackdropFilter 高斯模糊是一個實用且友善的 Widget,可靈活應用在項目中;小菜對此研究還不夠深入,有問題的話請多多指導!

來源:阿策小和尚

繼續閱讀