【Flutter】操作结果动画实现

一、动画代码

1.成功动画

import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:math' as Math;

/*成功*/
class TickSuccess extends StatefulWidget {
  final double width;
  final Color color;
  final double lineWidth;
  final int animationPeriod; // 动画播放周期(影响动画速度)
  TickSuccess({
    Key key,
    this.width = 200,
    this.color,
    this.lineWidth,
    this.animationPeriod = 2000,
  }) : super(key: key);

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

class _TickSuccessState extends State<TickSuccess>
    with TickerProviderStateMixin {
  AnimationController _animationController;
  Animation<double> _animation;

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

    /// AnimationController在给定的时间段内线性的生成从0.0到1.0(默认区间)的数字,总时长
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: widget.animationPeriod),
    );

    Animation curveAnimation =
        new CurvedAnimation(parent: _animationController, curve: Curves.ease);

    /// 设置输出范围值,这里分两个动画步骤,0 -> 1表示画圆, 1 -> 1.5 表示画钩
    _animation = Tween<double>(begin: 0.0, end: 1.5).animate(curveAnimation);

    _animationController.addListener(() {
      setState(() {});
    });

    /// 开始动画
    _animationController.forward();
  }

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

  @override
  Widget build(BuildContext context) {
    return Container(
      width: widget.width,
      height: widget.width,
      child: CustomPaint(
        painter: _TickSuccessPainter(_animation.value,
            color: widget.color, lineWidth: widget.lineWidth),
      ),
    );
  }
}

class _TickSuccessPainter extends CustomPainter {
  Paint viewPaint;
  Path path;
  final double progress;
  final Color color;
  final double lineWidth;

  _TickSuccessPainter(this.progress, {this.color, this.lineWidth}) {
    viewPaint = Paint()
      ..strokeCap = StrokeCap.round
      ..isAntiAlias = true
      ..color = color ?? Colors.green
      ..style = PaintingStyle.stroke
      ..strokeWidth = lineWidth ?? 5.0;

    path = Path();
  }

  // Method to convert degree to radians
  num degToRad(num deg) => deg * (Math.pi / 180.0);

  @override
  void paint(Canvas canvas, Size size) {
    /// 原点x,y坐标
    double x = size.width / 2;
    double y = size.width / 2;
    double radius = size.width / 3;
    double len = 1 * (-radius / 6) + radius / 2;

    path.moveTo(x, y);

    /// 背景圆
    path.addArc(
        Rect.fromCenter(
          center: Offset(x, y),
          width: radius * 2,
          height: radius * 2,
        ),
        degToRad(0),
        degToRad(360));

    /// 打钩
    path.moveTo(x - radius / 2, y);
    path.lineTo(x - radius / 6, y + len);
    path.lineTo(x + radius * 1 / 2, y - radius * 1 / 3);

    /// 路径动画
    PathMetrics pathMetrics = path.computeMetrics();

    List list = pathMetrics.toList(); //数组长度与执行moveTo次数相关

    /// 画圆
    PathMetric circularPathMetric = list[0];
    Path circularExtractPath = circularPathMetric.extractPath(
      0.0,
      circularPathMetric.length * progress,
    );
    canvas.drawPath(circularExtractPath, viewPaint);

    /// 画钩
    PathMetric tickPathMetric = list[1];
    Path tickExtractPath = tickPathMetric.extractPath(
      0.0,
      tickPathMetric.length * (progress - 0.5),
    );
    canvas.drawPath(tickExtractPath, viewPaint);
  }

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

2.失败动画

import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:math' as Math;

/*失败*/
class CrossFail extends StatefulWidget {
  final double width;
  final Color color;
  final double lineWidth;
  final int animationPeriod; // 动画播放周期(影响动画速度)
  CrossFail(
      {Key key,
      this.width = 200,
      this.color,
      this.lineWidth,
      this.animationPeriod = 2000})
      : super(key: key);

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

class _CrossFailState extends State<CrossFail> with TickerProviderStateMixin {
  AnimationController _animationController;
  Animation<double> _animation;

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

    /// AnimationController在给定的时间段内线性的生成从0.0到1.0(默认区间)的数字,总时长
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: widget.animationPeriod),
    );

    Animation curveAnimation =
        new CurvedAnimation(parent: _animationController, curve: Curves.ease);

    /// 设置输出范围值,这里分两个动画步骤,0 -> 1表示画圆, 1 -> 1.5 表示画叉
    _animation = Tween<double>(begin: 0.0, end: 1.5).animate(curveAnimation);

    _animationController.addListener(() {
      setState(() {});
    });

    /// 开始动画
    _animationController.forward();
  }

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

  @override
  Widget build(BuildContext context) {
    return Container(
        width: widget.width,
        height: widget.width,
        child: CustomPaint(
          painter: _CrossFailPainter(_animation.value,
              color: widget.color, lineWidth: widget.lineWidth),
        ));
  }
}

class _CrossFailPainter extends CustomPainter {
  Paint viewPaint;
  Path path;
  final double progress;
  final Color color;
  final double lineWidth;

  _CrossFailPainter(this.progress, {this.color, this.lineWidth}) {
    viewPaint = Paint()
      ..strokeCap = StrokeCap.round
      ..isAntiAlias = true
      ..color = color ?? Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = lineWidth ?? 5.0;

    path = Path();
  }

  // Method to convert degree to radians
  num degToRad(num deg) => deg * (Math.pi / 180.0);

  @override
  void paint(Canvas canvas, Size size) {
    /// 原点x,y坐标
    double x = size.width / 2;
    double y = size.width / 2;
    double radius = size.width / 3;

    path.moveTo(x, y);

    /// 背景圆
    path.addArc(
        Rect.fromCenter(
          center: Offset(x, y),
          width: radius * 2,
          height: radius * 2,
        ),
        degToRad(0),
        degToRad(360));

    path.moveTo(x - radius / 2, x - radius / 2);
    path.lineTo(x + radius / 2, x + radius / 2);

    path.moveTo(x + radius / 2, x - radius / 2);
    path.lineTo(x - radius / 2, x + radius / 2);

    /// 路径动画
    PathMetrics pathMetrics = path.computeMetrics();

    List list = pathMetrics.toList();

    /// 画圆
    PathMetric circularPathMetric = list[0];
    Path circularExtractPath = circularPathMetric.extractPath(
      0.0,
      circularPathMetric.length * progress,
    );
    canvas.drawPath(circularExtractPath, viewPaint);

    /// 画叉
    PathMetric tickPathMetric = list[1];
    Path tickExtractPath = tickPathMetric.extractPath(
      0.0,
      tickPathMetric.length * (progress - 0.5),
    );
    canvas.drawPath(tickExtractPath, viewPaint);

    PathMetric linePathMetric = list[2];
    Path lineExtractPath = linePathMetric.extractPath(
      0.0,
      linePathMetric.length * progress,
    );
    canvas.drawPath(lineExtractPath, viewPaint);
  }

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

二、使用

import 'package:flutter/material.dart';
import '../../compontents/animation/cross_fail.dart';
import '../../compontents/animation/tick_success.dart';

/*组件演示*/
class Demo extends StatefulWidget {
  Demo({Key key}) : super(key: key);

  @override
  State<Demo> createState() => _DemoState();
}

class _DemoState extends State<Demo> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
      margin: EdgeInsets.all(40),
      child: Wrap(
          spacing: 20,
          crossAxisAlignment: WrapCrossAlignment.center,
          children: <Widget>[
            TickSuccess(width: 40),
            TickSuccess(
              width: 80,
              color: Colors.green[700],
              animationPeriod: 3000,
            ),
            TickSuccess(
              width: 120,
              color: Colors.green[900],
              lineWidth: 10,
              animationPeriod: 4000,
            ),
            CrossFail(width: 40),
            CrossFail(
              width: 80,
              color: Colors.red[700],
              animationPeriod: 3000,
            ),
            CrossFail(
              width: 120,
              color: Colors.red[900],
              lineWidth: 10,
              animationPeriod: 4000,
            ),
          ]),
    ));
  }
}

三、动画演示

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter 加载中动画是指在数据加载或处理过程中为用户提供的视觉反馈。在 Flutter 中,我们可以使用多种方法来实现加载中动画。 一种常见的方式是使用 Flutter 自带的 CircularProgressIndicator 组件。这是一个圆形进度指示器,可以显示加载进度的百分比。我们可以通过设置 value 属性来控制进度的大小,通常可以将它与 FutureBuilder 或 StreamBuilder 结合使用来显示网络请求或异步操作的进度。此外,我们还可以通过设置颜色和线条粗细等属性来自定义 CircularProgressIndicator 的外观。 除了 CircularProgressIndicator,Flutter 还提供了许多其他的加载中动画,比如 LinearProgressIndicator、RefreshIndicator 等。LinearProgressIndicator 是一个线性进度指示器,适用于表示长时间操作的进度。RefreshIndicator 则是下拉刷新的效果,用户在列表或页面顶部下拉时,会出现一个圈圈的加载动画,表示正在刷新数据。 除了使用预置的加载中动画组件,我们还可以通过自定义动画实现更丰富的加载中效果。Flutter 提供了强大的动画库和组件,如 AnimatedContainer、AnimationController 等,使得创建自定义加载中动画变得更加方便。 总之,Flutter 提供了各种加载中动画的选择,无论是使用预置组件还是自定义动画,都可以根据项目需求和个人喜好来实现加载中效果。这些加载中动画可以提升用户体验,让用户在等待数据加载时感到更舒适和愉悦。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值