likes
comments
collection
share

flutter-高斯模糊效果

作者站长头像
站长
· 阅读数 45

分享两种高斯模糊效果的实现

主要变量

double width = 1.0.sw - 32.w;
double top = 0.4.sh;
double height = 90.h;
double radius = 16.w;

前期思路

背景色透明

一开始想的是用Container中背景色的透明度

Container(
  height: height,
  width: width,
  alignment: Alignment.center,
  decoration: BoxDecoration(
      color: Color.fromRGBO(255, 255, 255, 0.1),
      borderRadius: BorderRadius.all(Radius.circular(radius))
  ),
  child: Text(
    '高斯模糊',
    style: TextStyle(
        color: Colors.white
    ),
  ),
)

效果如下

flutter-高斯模糊效果 额,透明度是有了,但模糊一点都不模糊

加上模糊

网上说可以通过BackdropFilter来实现高斯模糊,大家可以通过这这篇文章简单理解一下# Flutter BackdropFilter 使用介绍

BackdropFilter可以用于对背景图片进行高斯模糊设置或者矩阵变换,除了图片以外BackdropFilter还能对任何子widget进行高斯模糊设置。BackdropFilter的定义如下:

BackdropFilter({Key key, 
         @required ImageFilter filter,
         Widget child })

OK,我们简单的套上试试

BackdropFilter(
  filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///整体模糊度
  child: Container(
    height: height,
    width: width,
    alignment: Alignment.center,
    decoration: BoxDecoration(
        color: Color.fromRGBO(255, 255, 255, 0.1),///背景透明
        borderRadius: BorderRadius.all(Radius.circular(radius))///圆角
    ),
    child: Text(
      '高斯模糊',
      style: TextStyle(
          color: Colors.white
      ),
    ),
  ),
)
flutter-高斯模糊效果

结果整个屏幕全部模糊了,虽然模糊是模糊了,但不是我想要的效果,我只想子组件范围内的模糊,那是否可以通过限定宽高来确定范围呢?我们来试一试

SizedBox(
  height: height,
  width: width,
  child: BackdropFilter(
    filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///整体模糊度
    child: Container(
      height: height,
      width: width,
      alignment: Alignment.center,
      decoration: BoxDecoration(
          color: Color.fromRGBO(255, 255, 255, 0.1),
          borderRadius: BorderRadius.all(Radius.circular(radius))
      ),
      child: Text(
        '高斯模糊',
        style: TextStyle(
            color: Colors.white
        ),
      ),
    ),
  ),
)

约束

但效果和上面一样,没有任何缩小范围,看来不是可以通过外部简单约束就可以的,后来通过搜索资料,才知道外层必须加上Clip通过剪切,才可以约束到子组件范围。我们裁剪的同时稍微调整一下宽高和背景色,让质感更好一些

ClipRRect(
  child: BackdropFilter(
    filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///整体模糊度
    child: Container(
      height: height,
      width: width,
      alignment: Alignment.center,
      decoration: BoxDecoration(
          color: Color.fromRGBO(175, 175, 175, 0.1),///大背景深色,组件颜色偏浅就会更好
          borderRadius: BorderRadius.all(Radius.circular(radius))
      ),
      child: Text(
        '高斯模糊',
        style: TextStyle(
            color: Colors.white
        ),
      ),
    ),
  ),
)
flutter-高斯模糊效果

阴影

到目前一切还好,但我还想加上阴影,于是自然而然就在Container的描述里添加阴影参数

ClipRRect(
  child: BackdropFilter(
    filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///整体模糊度
    child: Container(
      height: height,
      width: width,
      alignment: Alignment.center,
      decoration: BoxDecoration(
          color: Color.fromRGBO(175, 175, 175, 0.1),
          borderRadius: BorderRadius.all(Radius.circular(radius)),
          boxShadow: [
            BoxShadow
              (
                color:  Color.fromRGBO(22, 23, 26, 0.5),
                offset: Offset(2, 4), // 偏移量
                blurRadius: 2
            )]
      ),
      child: Text(
        '高斯模糊',
        style: TextStyle(
            color: Colors.white
        ),
      ),
    ),
  ),
)

就变成这样了flutter-高斯模糊效果

第一种方案

原来是因为透明度,所以本来被覆盖的阴影显示出来,而且因为外部裁剪,所以外部阴影又被裁掉,唉,再次查资料,发现了一种方法,就是通过自定义画笔,通过设定好的路径,画一条阴影出来。在这里贴出相关代码

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class CustomClipperOval extends CustomClipper<RRect> {
  final double radius;
  final double width;
  final double height;

  CustomClipperOval({
    required this.radius,
    required this.width,
    required this.height,
  });
  ///此处应和你想要高斯模糊组件的大小一致
  @override
  RRect getClip(Size size) {
    ///左、上、右、下,圆角的两个半径,坐标原点是屏幕左上角,坐标系往右下
    ///因为外部设置了左边距,所以左起点要有边距距离,顶部为0,右边要去掉左部分边距
    return RRect.fromLTRBXY((1.0.sw - width)/2, 0, width - (1.0.sw - width)/2, height, radius, radius);
  }

  @override
  bool shouldReclip(CustomClipper<RRect> oldClipper) {
    return false;
  }
}

class ClipOvalShadow extends StatelessWidget {
  final Shadow shadow;
  final CustomClipper<RRect> clipper;
  final double radius;
  final Widget child;

  ClipOvalShadow({
    required this.shadow,
    required this.clipper,
    required this.radius,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    ///开始自定义绘制
    return CustomPaint(
      ///画阴影
      painter: _ClipOvalShadowPainter(
        clipper: clipper,
        shadow: shadow,
      ),
      ///切割内部
      child: ClipRRect(clipper: clipper,borderRadius: BorderRadius.all(Radius.circular(radius)), child: child,),
    );
  }
}

class _ClipOvalShadowPainter extends CustomPainter {
  final Shadow shadow;
  final CustomClipper<RRect> clipper;

  _ClipOvalShadowPainter({required this.shadow, required this.clipper});

  @override
  void paint(Canvas canvas, Size size) {
    var paint = shadow.toPaint();
    ///根据路径画阴影
    var clipRect = clipper.getClip(size).shift(Offset(0, 0));
    canvas.drawRRect(clipRect, paint);
  }

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

外部调用

ClipOvalShadow(
  radius: radius,
  ///阴影设置
  shadow: BoxShadow(
      color: Color.fromRGBO(22, 23, 26, 0.5),
      offset: Offset(1.0, 1.0),
      blurRadius: 20,
      blurStyle: BlurStyle.outer///外部阴影,内部不填充
  ),
  ///路径
  clipper: CustomClipperOval(radius: radius,width: width,height: height),
  child: Center(
    child: ClipRRect(
      child:BackdropFilter(///背景过滤器
        filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///整体模糊度
        child: Container(///主体背景
          width: width,
          height: height,
          alignment: Alignment.center,
          decoration: BoxDecoration(
            color: Color.fromRGBO(175, 175, 175, 0.1),///背景透明度
            borderRadius: BorderRadius.all(Radius.circular(radius)),
          ),
          child: Text(
            '高斯模糊',
            style: TextStyle(
                color: Colors.white
            ),
          ),
        ),
      ),
    ),
  ),
)

效果如下

这里特意说一下阴影类型blurStyle,如果不特意设置阴影类型,默认的情况就是这个效果

默认阴影类型外部阴影,内部不填充
flutter-高斯模糊效果flutter-高斯模糊效果

第二种方案

我搞好后突然想到,子组件加阴影不行,我在父组件加是不是就可以?说干就干

Container(
  height: height,
  width: width,
  decoration: BoxDecoration(
      color: Colors.transparent,
      borderRadius: BorderRadius.all(Radius.circular(radius)),
      boxShadow: [BoxShadow(
          color: Color.fromRGBO(22, 23, 26, 0.5),
          offset: Offset(1.0, 1.0),
          blurRadius: 20,
          blurStyle: BlurStyle.outer///外部阴影,内部不填充
      )]
  ),
  child: ClipRRect(
    child: BackdropFilter(///背景过滤器
      filter: ImageFilter.blur(sigmaX: 10.0,sigmaY: 10.0),///模糊度
      child: Container(///主体背景
        height: height,
        width: width,
        alignment: Alignment.center,
        decoration: BoxDecoration(
          color: Color.fromRGBO(175, 175, 175, 0.1),
          borderRadius: BorderRadius.all(Radius.circular(16.w)),
        ),
        child: Text(
          '高斯模糊',
          style: TextStyle(
              color: Colors.white
          ),
        ),
      ),
    ),
  ),
)

但效果不是很完美,圆角没有完全匹配,通过修改父组件底色可以看到圆角并没有生效

未改变颜色改变底色
flutter-高斯模糊效果flutter-高斯模糊效果

这里也有两种方法,一个是父组件剪裁,一个是

第一种方法:父组件添加裁剪参数

clipBehavior: Clip.antiAlias,

第二种方法:ClipRRect自带的圆角设置

borderRadius: BorderRadius.all(Radius.circular(radius)),

两种效果是一样的

flutter-高斯模糊效果

复用组件

为了方便复用,我把第二种高斯模糊方法单独做一个组件出来

import 'dart:ui';
import 'package:flutter/material.dart';

class Acrylic extends StatefulWidget {

  ///阴影颜色
  final Color shadowColor;
  ///背景颜色
  final Color bgColor;
  ///背景模糊度
  final double blur;
  ///组件高度
  final double height;
  ///组件宽度
  final double width;
  ///组件是否有圆角
  final BorderRadiusGeometry? borderRadius;
  ///阴影的高斯模糊
  final double blurRadius;
  ///子组件
  final Widget child;

  const Acrylic({
    Key? key,
    this.shadowColor = const Color.fromRGBO(22, 23, 26, 0.5),
    this.bgColor = const Color.fromRGBO(175, 175, 175, 0.1),
    this.blur = 10.0,
    required this.height,
    required this.width,
    this.borderRadius,
    this.blurRadius = 20.0,
    required this.child,
  }) : super(key: key);

  @override
  State<Acrylic> createState() => _AcrylicState();
}

class _AcrylicState extends State<Acrylic> {

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      children: [
        Container(
          height: widget.height,
          clipBehavior: Clip.antiAlias,
          decoration: BoxDecoration(
              color: Colors.transparent,
              borderRadius: widget.borderRadius,
              boxShadow: [BoxShadow(
                  color: widget.shadowColor,
                  blurRadius: widget.blurRadius,
                  blurStyle: BlurStyle.outer///外部阴影,内部不填充
              )]
          ),
          child: ClipRRect(
            child:BackdropFilter(///背景过滤器
              filter: ImageFilter.blur(sigmaX: widget.blur,sigmaY: widget.blur),///模糊度
              child: Container(///主体背景
                width: widget.width,
                height: widget.height,
                decoration: BoxDecoration(
                    color: widget.bgColor,
                    borderRadius: widget.borderRadius
                ),
                child: widget.child,
              ),
            ),
          ),
        ),

      ],
    );
  }
}

外部调用

Acrylic(
    height: height,
    width: width,
    borderRadius: BorderRadius.all(Radius.circular(radius)),
    child: Container(///主体内部不会模糊
      alignment: Alignment.center,
      child: Text(
        '高斯模糊',
        style: TextStyle(
            color: Colors.white
        ),
      ),
    )
)

最后

我把几种方法都放在一起,想偷懒的可以直接下载我的工具箱 flutter-高斯模糊效果

转载自:https://juejin.cn/post/7211008551762067517
评论
请登录