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,
    );
  }


}
相关推荐
一只大侠的侠14 分钟前
Flutter开源鸿蒙跨平台训练营 Day 3
flutter·开源·harmonyos
一只大侠的侠1 小时前
【Harmonyos】Flutter开源鸿蒙跨平台训练营 Day 2 鸿蒙跨平台开发环境搭建与工程实践
flutter·开源·harmonyos
微祎_2 小时前
Flutter for OpenHarmony:构建一个 Flutter 平衡球游戏,深入解析动画控制器、实时物理模拟与手势驱动交互
flutter·游戏·交互
ZH15455891313 小时前
Flutter for OpenHarmony Python学习助手实战:面向对象编程实战的实现
python·学习·flutter
renke33644 小时前
Flutter for OpenHarmony:构建一个 Flutter 色彩调和师游戏,RGB 空间探索、感知色差计算与视觉认知训练的工程实现
flutter·游戏
王码码20354 小时前
Flutter for OpenHarmony 实战之基础组件:第三十一篇 Chip 系列组件 — 灵活的标签化交互
android·flutter·交互·harmonyos
ujainu6 小时前
Flutter + OpenHarmony 实现经典打砖块游戏开发实战—— 物理反弹、碰撞检测与关卡系统
flutter·游戏·openharmony·arkanoid·breakout
微祎_6 小时前
构建一个 Flutter 点击速度测试器:深入解析实时交互、性能度量与响应式 UI 设计
flutter·ui·交互
王码码20356 小时前
Flutter for OpenHarmony 实战之基础组件:第二十七篇 BottomSheet — 动态底部弹窗与底部栏菜单
android·flutter·harmonyos
ZH15455891317 小时前
Flutter for OpenHarmony Python学习助手实战:Web开发框架应用的实现
python·学习·flutter