如何在 Flutter 中创建 Wave 小部件

如何解决如何在 Flutter 中创建 Wave 小部件

我们知道波浪动画效果,所以我想在我的 Flutter 应用程序中使用这个效果

如何创建一个小部件,用于在颤动中显示具有自定义颜色和模糊效果的波浪。

我想要这样的东西

enter image description here

解决方法

为了达到这个目的,首先创建 WaveWidget

import 'dart:math';
import 'package:flutter/widgets.dart';
import 'package:prokit_flutter/main/utils/wave/config.dart';

class WaveWidget extends StatefulWidget {
  final Config config;
  final Size size;
  final double waveAmplitude;
  final double wavePhase;
  final double waveFrequency;
  final double heightPercentange;
  final int duration;
  final Color backgroundColor;

  WaveWidget({
    @required this.config,this.duration = 6000,@required this.size,this.waveAmplitude = 20.0,this.waveFrequency = 1.6,this.wavePhase = 10.0,this.backgroundColor,this.heightPercentange = 0.2,});

  @override
  State<StatefulWidget> createState() => _WaveWidgetState();
}

class _WaveWidgetState extends State<WaveWidget> with TickerProviderStateMixin {
  List<AnimationController> _waveControllers;
  List<Animation<double>> _wavePhaseValues;

  List<double> _waveAmplitudes = [];
  Map<Animation<double>,AnimationController> valueList;

  _initAnimations() {
    if (widget.config.colorMode == ColorMode.custom) {
      _waveControllers =
          (widget.config as CustomConfig).durations.map((duration) {
        _waveAmplitudes.add(widget.waveAmplitude + 10);
        return AnimationController(
            vsync: this,duration: Duration(milliseconds: duration));
      }).toList();

      _wavePhaseValues = _waveControllers.map((controller) {
        CurvedAnimation _curve =
            CurvedAnimation(parent: controller,curve: Curves.easeInOut);
        Animation<double> value = Tween(
          begin: widget.wavePhase,end: 360 + widget.wavePhase,).animate(
          _curve,);
        value.addStatusListener((status) {
          switch (status) {
            case AnimationStatus.completed:
              controller.reverse();
              break;
            case AnimationStatus.dismissed:
              controller.forward();
              break;
            default:
              break;
          }
        });
        controller.forward();
        return value;
      }).toList();
    }
  }

  _buildPaints() {
    List<Widget> paints = [];
    if (widget.config.colorMode == ColorMode.custom) {
      List<Color> _colors = (widget.config as CustomConfig).colors;
      List<List<Color>> _gradients = (widget.config as CustomConfig).gradients;
      Alignment begin = (widget.config as CustomConfig).gradientBegin;
      Alignment end = (widget.config as CustomConfig).gradientEnd;
      for (int i = 0; i < _wavePhaseValues.length; i++) {
        paints.add(
          Container(
            child: CustomPaint(
              painter: _CustomWavePainter(
                color: _colors == null ? null : _colors[i],gradient: _gradients == null ? null : _gradients[i],gradientBegin: begin,gradientEnd: end,heightPercentange:
                    (widget.config as CustomConfig).heightPercentages[i],repaint: _waveControllers[i],waveFrequency: widget.waveFrequency,wavePhaseValue: _wavePhaseValues[i],waveAmplitude: _waveAmplitudes[i],blur: (widget.config as CustomConfig).blur,),size: widget.size,);
      }
    }
    return paints;
  }

  _disposeAnimations() {
    _waveControllers.forEach((controller) {
      controller.dispose();
    });
  }

  @override
  void initState() {
    super.initState();
    _initAnimations();
  }

  @override
  void dispose() {
    _disposeAnimations();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: widget.backgroundColor,child: Stack(
        children: _buildPaints(),);
  }
}

/// Meta data of layer
class Layer {
  final Color color;
  final List<Color> gradient;
  final MaskFilter blur;
  final Path path;
  final double amplitude;
  final double phase;

  Layer({
    this.color,this.gradient,this.blur,this.path,this.amplitude,this.phase,});
}

class _CustomWavePainter extends CustomPainter {
  final ColorMode colorMode;
  final Color color;
  final List<Color> gradient;
  final Alignment gradientBegin;
  final Alignment gradientEnd;
  final MaskFilter blur;

  double waveAmplitude;

  Animation<double> wavePhaseValue;

  double waveFrequency;

  double heightPercentange;

  double _tempA = 0.0;
  double _tempB = 0.0;
  double viewWidth = 0.0;
  Paint _paint = Paint();

  _CustomWavePainter(
      {this.colorMode,this.color,this.gradientBegin,this.gradientEnd,this.heightPercentange,this.waveFrequency,this.wavePhaseValue,this.waveAmplitude,Listenable repaint})
      : super(repaint: repaint);

  _setPaths(double viewCenterY,Size size,Canvas canvas) {
    Layer _layer = Layer(
      path: Path(),color: color,gradient: gradient,blur: blur,amplitude: (-1.6 + 0.8) * waveAmplitude,phase: wavePhaseValue.value * 2 + 30,);

    _layer.path.reset();
    _layer.path.moveTo(
        0.0,viewCenterY +
            _layer.amplitude * _getSinY(_layer.phase,waveFrequency,-1));
    for (int i = 1; i < size.width + 1; i++) {
      _layer.path.lineTo(
          i.toDouble(),viewCenterY +
              _layer.amplitude * _getSinY(_layer.phase,i));
    }

    _layer.path.lineTo(size.width,size.height);
    _layer.path.lineTo(0.0,size.height);
    _layer.path.close();
    if (_layer.color != null) {
      _paint.color = _layer.color;
    }
    if (_layer.gradient != null) {
      var rect = Offset.zero &
          Size(size.width,size.height - viewCenterY * heightPercentange);
      _paint.shader = LinearGradient(
              begin: gradientBegin == null
                  ? Alignment.bottomCenter
                  : gradientBegin,end: gradientEnd == null ? Alignment.topCenter : gradientEnd,colors: _layer.gradient)
          .createShader(rect);
    }
    if (_layer.blur != null) {
      _paint.maskFilter = _layer.blur;
    }

    _paint.style = PaintingStyle.fill;
    canvas.drawPath(_layer.path,_paint);
  }

  @override
  void paint(Canvas canvas,Size size) {
    double viewCenterY = size.height * (heightPercentange + 0.1);
    viewWidth = size.width;
    _setPaths(viewCenterY,size,canvas);
  }

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

  double _getSinY(
      double startradius,double waveFrequency,int currentposition) {
    if (_tempA == 0) {
      _tempA = pi / viewWidth;
    }
    if (_tempB == 0) {
      _tempB = 2 * pi / 360.0;
    }

    return (sin(
        _tempA * waveFrequency * (currentposition + 1) + startradius * _tempB));
  }
}

制作 Config 类进行配置

import 'dart:ui';

import 'package:flutter/widgets.dart';

enum ColorMode {
  /// Waves with *single* **color** but different **alpha** and **amplitude**.
  single,/// Waves using *random* **color**,**alpha** and **amplitude**.
  random,/// Waves' colors must be set,and [colors]'s length must equal with [layers]
  custom,}

abstract class Config {
  final ColorMode colorMode;

  Config({this.colorMode});

  void throwNullError(String colorModeStr,String configStr) {
    throw FlutterError(
        'When using `ColorMode.$colorModeStr`,`$configStr` must be set.');
  }
}

class CustomConfig extends Config {
  final List<Color> colors;
  final List<List<Color>> gradients;
  final Alignment gradientBegin;
  final Alignment gradientEnd;
  final List<int> durations;
  final List<double> heightPercentages;
  final MaskFilter blur;

  CustomConfig({
    this.colors,this.gradients,@required this.durations,@required this.heightPercentages,})  : assert(() {
          if (colors == null && gradients == null) {
            throw 'colors` or `gradients';
          }
          return true;
        }()),assert(() {
          if (gradients == null &&
              (gradientBegin != null || gradientEnd != null)) {
            throw FlutterError(
                'You set a gradient direction but forgot setting `gradients`.');
          }
          return true;
        }()),assert(() {
          if (durations == null) {
            throw 'durations';
          }
          return true;
        }()),assert(() {
          if (heightPercentages == null) {
            throw 'heightPercentages';
          }
          return true;
        }()),assert(() {
          if (colors != null) {
            if (colors.length != durations.length ||
                colors.length != heightPercentages.length) {
              throw FlutterError(
                  'Length of `colors`,`durations` and `heightPercentages` must be equal.');
            }
          }
          return true;
        }()),assert(colors == null || gradients == null,'Cannot provide both colors and gradients.'),super(colorMode: ColorMode.custom);
}

/// todo
class RandomConfig extends Config {
  RandomConfig() : super(colorMode: ColorMode.random);
}

/// todo
class SingleConfig extends Config {
  SingleConfig() : super(colorMode: ColorMode.single);
}

在屏幕中导入这两个类并像这样使用它们

class WaveScreen extends StatefulWidget {
  static String tag = '/WaveScreen';

  @override
  WaveScreenState createState() => WaveScreenState();
}

class WaveScreenState extends State<WaveScreen> {
  @override
  Widget build(BuildContext context) {
    changeStatusColor(appColorPrimary);
    return Scaffold(
      appBar: appBar(context,"Wave Widget"),body: WaveWidget(
        config: CustomConfig(
          gradients: [
            [Color(0xFF676B76),Color(0xEE676B76)],[Color(0xFFECB099),Color(0xEEECB099)],[Color(0xFFdea34b),Color(0xEEdea34b)],[Color(0xFFA3CEB6),Color(0xEEA3CEB6)],],durations: [35000,19440,10800,6000],heightPercentages: [0.20,0.23,0.25,0.30],blur: MaskFilter.blur(BlurStyle.solid,10),gradientBegin: Alignment.bottomLeft,gradientEnd: Alignment.topRight,waveAmplitude: 0,backgroundColor: Colors.white,size: Size(double.infinity,double.infinity),);
  }
}

enter image description here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-