likes
comments
collection
share

App高级感营造之 高斯模糊

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

效果

类似毛玻璃,或者马赛克的效果。我们可以用它来提升app背景的整体质感,或者给关键信息打码。

App高级感营造之  高斯模糊

App高级感营造之  高斯模糊

源代码

import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // 高斯模糊的第一种写法 ImageFiltered 包裹要模糊的组件

  /// 将子组件进行高斯模糊
  /// [child] 要模糊的子组件
  Widget _imageFilteredWidget1({required Widget child, double sigmaValue = 1}) {
    return ImageFiltered(
      imageFilter: ImageFilter.blur(sigmaX: sigmaValue, sigmaY: sigmaValue),
      child: child,
    );
  }

  /// 使用第一种模糊方式的案例
  Widget _demo1() {
    return Container(
      padding: const EdgeInsets.all(50),
      color: Colors.blue.shade100,
      width: double.infinity,
      child: Column(
        children: [
          _imageFilteredWidget1(
            child: SizedBox(
              width: 150,
              child: Image.asset(
                "assets/images/bz1.jpg",
                fit: BoxFit.fitHeight,
              ),
            ),
          ),
          const SizedBox(height: 100),
          _imageFilteredWidget1(
              child: const Text(
                "测试高斯模糊",
                style: TextStyle(fontSize: 30, color: Colors.blueAccent),
              ),
              sigmaValue: 2)
        ],
      ),
    );
  }

  /// 利用  BackdropFilter 做高斯模糊
  _backdropFilterWidget2({
    required Widget child,
    double sigmaValueX = 1,
    double sigmaValueY = 1,
  }) {
    return ClipRect(
      child: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: sigmaValueX, sigmaY: sigmaValueY),
        child: child,
      ),
    );
  }

  ///
  Widget _demo2() {
    return SizedBox(
      width: double.infinity,
      height: double.infinity,
      child: Stack(
        alignment: Alignment.center,
        children: [
          Positioned.fill(
            child: Image.asset(
              "assets/images/bz1.jpg",
              fit: BoxFit.fill,
            ),
          ),
          Positioned(
            child: _backdropFilterWidget2(
                sigmaValueX: _sigmaValueX,
                sigmaValueY: _sigmaValueY,
                child: Container(
                  width: MediaQuery.of(context).size.width - 100,
                  height: MediaQuery.of(context).size.height / 2,
                  alignment: Alignment.center,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(20),
                    color: const Color(0x90ffffff),
                  ),
                  child: const Text(
                    "高斯模糊",
                    style: TextStyle(fontSize: 30, color: Colors.white),
                  ),
                )),
            top: 20,
          ),
          _slider(
            bottomMargin: 200,
            themeColors: Colors.yellow,
            title: '横向模糊度',
            valueAttr: _sigmaValueX,
            onChange: (double value) {
              setState(() {
                _sigmaValueX = value;
              });
            },
          ),
          _slider(
            bottomMargin: 160,
            themeColors: Colors.blue,
            title: '纵向模糊度',
            valueAttr: _sigmaValueY,
            onChange: (double value) {
              setState(() {
                _sigmaValueY = value;
              });
            },
          ),
          _slider(
            bottomMargin: 120,
            themeColors: Colors.green,
            title: '同时调整:',
            valueAttr: _sigmaValue,
            onChange: (double value) {
              setState(() {
                _sigmaValue = value;
                _sigmaValueX = value;
                _sigmaValueY = value;
              });
            },
          ),
        ],
      ),
    );
  }

  Widget _slider({
    required String title,
    required double bottomMargin,
    required Color themeColors,
    required double valueAttr,
    required ValueChanged<double>? onChange,
  }) {
    return Positioned(
      bottom: bottomMargin,
      child: Row(
        children: [
          Text(title, style: TextStyle(color: themeColors, fontSize: 18)),
          SliderTheme(
            data: SliderThemeData(
              trackHeight: 20,
              activeTrackColor: themeColors.withOpacity(.7),
              thumbColor: themeColors,
              inactiveTrackColor: themeColors.withOpacity(.4)
            ),
            child: SizedBox(
              width: MediaQuery.of(context).size.width * 0.5,
              child: Slider(
                value: valueAttr,
                min: 0,
                max: 10,
                onChanged: onChange,
              ),
            ),
          ),
          SizedBox(
            width: 50,
            child: Text('${valueAttr.round()}',
                style: TextStyle(color: themeColors, fontSize: 18)),
          ),
        ],
      ),
    );
  }

  double _sigmaValueX = 10;
  double _sigmaValueY = 10;

  double _sigmaValue = 10;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: _demo2(),
    );
  }
}

实现原理

实现高斯模糊,在flutter中有两种方式:

ImageFiltered

它可以对其包裹的子组件施加高斯模糊,需要传入 ImageFilter 控制模糊程度,分为X Y两个方向的模糊,实际上就是对图片进行拉伸,数字越大,模糊效果越大。

/// 将子组件进行高斯模糊
/// [child] 要模糊的子组件
Widget _imageFilteredWidget1({required Widget child, double sigmaValue = 1}) {
  return ImageFiltered(
    imageFilter: ImageFilter.blur(sigmaX: sigmaValue, sigmaY: sigmaValue),
    child: child,
  );
}

BackDropFilter

同样需要一个 ImageFilter参数控制模糊度,与 ImageFilter的区别是,它会对它覆盖的组件整体模糊。 所以如果我们需要对指定的子组件进行模糊的话,需要再包裹一个ClipRect裁切。

/// 利用  BackdropFilter 做高斯模糊
_backdropFilterWidget2({required Widget child, double sigmaValue = 1}) {
  return ClipRect(
    child: BackdropFilter(
      filter: ImageFilter.blur(sigmaY: sigmaValue, sigmaX: sigmaValue),
      child: child,
    ),
  );
}

由于 BackdropFilter 会对其子组件进行图形处理,所以其子组件可能会变得更加消耗性能。因此,需要谨慎使用 BackdropFilter 组件。