Flutter---设备搜索动画效果

效果图:这个项目是动态效果

动画逻辑流程

Dart 复制代码
// 核心逻辑链
AnimationController(驱动)
    ↓
TweenSequence(定义动画序列)
    ↓
CurvedAnimation(应用动画曲线)
    ↓
AnimatedBuilder(监听变化)
    ↓
Widget树(重新构建)

时间线分析

Dart 复制代码
时间 (秒) | 0   | 1   | 2   | 3   | 4   |
圆1大小 | 0   | 100 | 200 | 300 | 0   |
圆2大小 | 100 | 200 | 300 | 0   | 100 |
圆3大小 | 200 | 300 | 0   | 100 | 200 |
圆4大小 | 300 | 0   | 100 | 200 | 300 |

任何时候都有3个圆可见,1个圆透明或大小为0

数据流向

Dart 复制代码
// 初始化阶段
initState()
    ↓
AnimationController创建
    ↓
TweenSequence定义动画路径
    ↓
动画对象绑定

// 运行阶段
_controller.repeat()
    ↓
值随时间变化 (0.0 → 1.0)
    ↓
TweenSequence计算当前值
    ↓
AnimatedBuilder重建UI

总结

Dart 复制代码
核心架构特点:
单一控制器:一个 AnimationController 控制所有动画

相位偏移:四个圆的时间相位各差25%

同步变化:大小和颜色同步变化

循环动画:使用 repeat() 实现无限循环

底层逻辑核心:
时间驱动:AnimationController 作为时间源

插值计算:TweenSequence 实现多段动画

响应式UI:AnimatedBuilder 监听变化

数据结构本质:
动画状态机:通过 Animation 对象管理状态

配置数据:硬编码在代码中的动画参数

渲染数据:实时计算的 size 和 color 值

关键点:

1.为什么有4个圆,但是视图中只有3个

2.重复代码过多,如何优化

代码实例

Dart 复制代码
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

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


class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin{


  late AnimationController _controller; //动画控制器

  late Animation<double> _sizeAnimation;
  late Animation<Color?> _colorAnimation;

  late Animation<double> _size1Animation;
  late Animation<Color?> _color1Animation;

  late Animation<double> _size2Animation;
  late Animation<Color?> _color2Animation;

  late Animation<double> _size3Animation;
  late Animation<Color?> _color3Animation;


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

    //初始化动画控制器
    _controller = AnimationController(
        vsync: this,
        duration: Duration(seconds: 4)
    );

    //TweenSequence - 动画序列 ;TweenSequenceItem - 序列项
    _sizeAnimation = TweenSequence<double>([
      TweenSequenceItem(tween:Tween<double>(begin: 0,end:100),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 100,end:200),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 200,end:300),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 300,end:0),
          weight: 1.0
      ),

    ]).animate(
      CurvedAnimation(parent: _controller, curve: Curves.linear),
    );

    _colorAnimation = TweenSequence<Color?>([
      TweenSequenceItem(
        tween: ColorTween(
          begin: Color(0xFF248EFF),
          end: Color(0xFF248EFF).withOpacity(0.7),
        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin:Color(0xFF248EFF).withOpacity(0.7),
          end: Color(0xFF248EFF).withOpacity(0.4),
        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin:Color(0xFF248EFF).withOpacity(0.4),
          end: Color(0xFF248EFF).withOpacity(0.1),
        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin: Colors.transparent,
          end: Colors.transparent,
        ),
        weight: 1.0,
      ),
    ]).animate(CurvedAnimation(parent: _controller, curve: Curves.linear));



    _size1Animation = TweenSequence<double>([
      TweenSequenceItem(tween:Tween<double>(begin: 100,end:200),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 200,end:300),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 300,end:0),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 0,end:100),
          weight: 1.0
      ),
    ]).animate(
      CurvedAnimation(parent: _controller, curve: Curves.linear),
    );

    _color1Animation = TweenSequence<Color?>([
      TweenSequenceItem(
        tween: ColorTween(
          begin: Color(0xFF248EFF).withOpacity(0.7),
          end: Color(0xFF248EFF).withOpacity(0.4),
        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin: Color(0xFF248EFF).withOpacity(0.4),
          end: Color(0xFF248EFF).withOpacity(0.1),

        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin: Colors.transparent,
          end: Colors.transparent,
        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin: Color(0xFF248EFF),
          end: Color(0xFF248EFF).withOpacity(0.7),

        ),
        weight: 1.0,
      ),
    ]).animate(CurvedAnimation(parent: _controller, curve: Curves.linear));


    _size2Animation = TweenSequence<double>([

      TweenSequenceItem(tween:Tween<double>(begin: 200,end:300),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 300,end:0),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 0,end:100),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 100,end:200),
          weight: 1.0
      ),
    ]).animate(
      CurvedAnimation(parent: _controller, curve: Curves.linear),
    );

    _color2Animation = TweenSequence<Color?>([
      TweenSequenceItem(
        tween: ColorTween(
          begin: Color(0xFF248EFF).withOpacity(0.4),
          end: Color(0xFF248EFF).withOpacity(0.1),
        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin: Colors.transparent,
          end: Colors.transparent,

        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin:Color(0xFF248EFF),
          end: Color(0xFF248EFF).withOpacity(0.7),
        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin: Color(0xFF248EFF).withOpacity(0.7),
          end: Color(0xFF248EFF).withOpacity(0.4),
        ),
        weight: 1.0,
      ),
    ]).animate(CurvedAnimation(parent: _controller, curve: Curves.linear));



    _size3Animation = TweenSequence<double>([
      TweenSequenceItem(tween:Tween<double>(begin: 300,end:0),
        weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 0,end:100),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 100,end:200),
          weight: 1.0
      ),
      TweenSequenceItem(tween:Tween<double>(begin: 200,end:300),
          weight: 1.0
      ),
    ]).animate(
      CurvedAnimation(parent: _controller, curve: Curves.linear),
    );


    _color3Animation = TweenSequence<Color?>([
      TweenSequenceItem(
        tween: ColorTween(
          begin: Colors.transparent,
          end: Colors.transparent,

        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin: Color(0xFF248EFF),
          end: Color(0xFF248EFF).withOpacity(0.7),

        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin:Color(0xFF248EFF).withOpacity(0.7),
          end: Color(0xFF248EFF).withOpacity(0.4),
        ),
        weight: 1.0,
      ),
      TweenSequenceItem(
        tween: ColorTween(
          begin:Color(0xFF248EFF).withOpacity(0.4),
          end: Color(0xFF248EFF).withOpacity(0.1),
        ),
        weight: 1.0,
      ),
    ]).animate(CurvedAnimation(parent: _controller, curve: Curves.linear));




   _controller.repeat(); //动画重复
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (context, child) {
            return Stack(
              alignment: Alignment.center, //关键:居中对齐
              children: [
                // 第4个圆 - 最大(在最底层)
                _buildAnimatedCircle(
                  size: _size3Animation.value,
                  color: _color3Animation.value,
                ),

                // 第3个圆 - 中大等(在中间层)
                _buildAnimatedCircle(
                  size: _size2Animation.value,
                  color: _color2Animation.value,
                ),

                // 第2个圆 - 中小等(在中间层)
                _buildAnimatedCircle(
                  size: _size1Animation.value,
                  color: _color1Animation.value,
                ),

                // 第1个圆 - 最小(在最上层)
                _buildAnimatedCircle(
                  size: _sizeAnimation.value,
                  color: _colorAnimation.value,
                ),

                Icon(Icons.search, color: Colors.white, size: 90),
              ],
            );
          },
        ),
      ),
    );
  }

  // ============================构建动画圆形的通用方法==========================
  Widget _buildAnimatedCircle({
    required double size,
    required Color? color,
    Widget? child,
  }) {
    return Container(
      width: size,
      height: size,
      decoration: BoxDecoration(
        color: color,
        shape: BoxShape.circle,
      ),
      child: child != null ? Center(child: child) : null,
    );
  }


}
相关推荐
恋猫de小郭32 分钟前
Flutter 3.44 发布啦,超级大版本更新!!!
android·flutter·ios
张3蜂1 小时前
Flutter macOS 安装文档
flutter·macos
Swuagg1 小时前
Flutter 架构实践:从 0 到 1 构建智能眼镜应用
flutter·架构
天天开发1 小时前
Flutter开发者该掌握的iOS隐私审核政策
flutter·ios·cocoa
leazer19 小时前
Flutter Windows 构建失败:.plugin_symlinks 符号链接异常的排查与修复
windows·flutter
小蜜蜂嗡嗡2 天前
flutter image_cropper截图控件布局顶到状态栏中问题
flutter
程序员老刘2 天前
跨平台开发地图:大厂统一底层,五月框架大乱斗谁在干实事?| 2026年5月
flutter·客户端
环信即时通讯云2 天前
环信Flutter UIKit适配鸿蒙实战指南
flutter·华为·harmonyos
用户536822100182 天前
flutter学习笔记 - Dart基本语法(一)
flutter
用户游民2 天前
Flutter Provider原理以及用法
前端·flutter