Flutter水波纹效果

一、预览

参考:Flutter 133: 图解自定义 ACEWaterButton 水波纹按钮

Flutter水波纹效果_第1张图片

二、分析

  • 内置圆为固定宽高矩形
  • 使用AnimationController获取时间进度
  • 外置圆为Paint绘制的动态宽高矩形,根据时间进度逐步更新透明度
  • Stack叠加内置矩形和外置矩形

1 内置矩形

Container(
  height: widget.innerHigh,
  width: widget.innerWidth,
  decoration: BoxDecoration(
    color: Colors.red,
    borderRadius: BorderRadius.circular(20.0)
  )
)

2 水波纹

(1) 动画

当创建一个AnimationController时,需要传递一个vsync参数,它接收一个TickerProvider类型的对象,它的职责是创建Ticker。通常我们会讲SingleTickerProvideStateMixin添加到State的定义中,然后将State对象作为vsync的值。

片段如下:

class _ACEWaterButtonState extends State
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    _controller = AnimationController(
        vsync: this, duration: widget.duration ?? Duration(milliseconds: 2000))
      ..repeat();
    super.initState();
  }

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

(2) 画水波纹矩形

class ACEWaterPainter extends CustomPainter {
  final double progress;
  final Color color;
  final double innerWidth;
  final double innerHigh;
  final double outerWidth;
  final double outerHigh;

  Paint _paint = Paint()..style = PaintingStyle.fill;

  ACEWaterPainter(this.progress, this.color, this.innerWidth, this.innerHigh, this.outerWidth, this.outerHigh);

  @override
  void paint(Canvas canvas, Size size) {
    _paint..color = color.withOpacity(1.0 - progress);

    double _changeW = innerWidth + (outerWidth - innerWidth) * progress;
    double _changeH = innerHigh + (outerHigh - innerHigh) * progress;

    canvas.translate(size.width/2, size.height/2);
    canvas.drawRRect(RRect.fromRectAndRadius(Rect.fromCenter(center: Offset(0, 0), width: _changeW, height: _changeH), Radius.circular(20.0)), _paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

三、完整代码

目前支持自定义如下参数

外置矩形宽高(可选,默认内置矩形2倍)

内置矩形宽高

颜色

单次时长(可选,默认2s)

import 'package:flutter/material.dart';

class ACEWaterButton extends StatefulWidget {
  final double innerWidth;
  final double innerHigh;
  final double? outerWidth;
  final double? outerHigh;
  final Color color;
  final Duration? duration;

  const ACEWaterButton(this.color,
      {this.innerWidth = 68.0, this.innerHigh = 48.0, this.duration, this.outerWidth, this.outerHigh});

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

class _ACEWaterButtonState extends State
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    _controller = AnimationController(
        vsync: this, duration: widget.duration ?? Duration(milliseconds: 2000))
      ..repeat();
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        return Stack(
          alignment: Alignment.center,
          children: [
            CustomPaint(
                size: Size(widget.outerWidth ?? widget.innerWidth * 2, widget.outerHigh ?? widget.innerHigh * 2),
                painter: ACEWaterPainter(_controller.value, widget.color,
                    widget.innerWidth, widget.innerHigh, widget.outerWidth ?? widget.innerWidth * 2, widget.outerHigh ?? widget.innerHigh * 2)
            ),
            Container(
              height: widget.innerHigh,
              width: widget.innerWidth,
              decoration: BoxDecoration(
                color: widget.color,
                borderRadius: BorderRadius.circular(20.0)
              ),
            ),
          ],
        );
      }
    );
  }
}

class ACEWaterPainter extends CustomPainter {
  final double progress;
  final Color color;
  final double innerWidth;
  final double innerHigh;
  final double outerWidth;
  final double outerHigh;

  Paint _paint = Paint()..style = PaintingStyle.fill;

  ACEWaterPainter(this.progress, this.color, this.innerWidth, this.innerHigh, this.outerWidth, this.outerHigh);

  @override
  void paint(Canvas canvas, Size size) {
    _paint..color = color.withOpacity(1.0 - progress);

    double _changeW = innerWidth + (outerWidth - innerWidth) * progress;
    double _changeH = innerHigh + (outerHigh - innerHigh) * progress;

    canvas.translate(size.width/2, size.height/2);
    canvas.drawRRect(RRect.fromRectAndRadius(Rect.fromCenter(center: Offset(0, 0), width: _changeW, height: _changeH), Radius.circular(20.0)), _paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

你可能感兴趣的:(flutter,android,动画)