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


}
相关推荐
雨季6661 小时前
Flutter 三端应用实战:OpenHarmony “微光笔记”——在灵感消逝前,为思想点一盏灯
开发语言·javascript·flutter·ui·dart
kirk_wang2 小时前
Flutter艺术探索-Flutter三方库鸿蒙适配实战:从原理到实践
flutter·移动开发·flutter教程·移动开发教程
晚霞的不甘3 小时前
Flutter for OpenHarmony 实现高级视差侧滑菜单:融合动效、模糊与交互动画的现代 UI 设计
flutter·ui·前端框架·交互·鸿蒙
晚霞的不甘4 小时前
Flutter for OpenHarmony构建全功能视差侧滑菜单系统:从动效设计到多页面导航的完整实践
前端·学习·flutter·microsoft·前端框架·交互
恋猫de小郭4 小时前
Flutter 在 Android 出现随机字体裁剪?其实是图层合并时的边界计算问题
android·flutter·ios
2501_944448005 小时前
Flutter for OpenHarmony 衣橱管家App实战 - 智能推荐实现
flutter
菜鸟小芯5 小时前
【开源鸿蒙跨平台开发先锋训练营】DAY8~DAY13 底部选项卡&我的页面功能实现
flutter·harmonyos
灰灰勇闯IT5 小时前
Flutter for OpenHarmony:悬浮按钮(FloatingActionButton)最佳实践 —— 强化核心操作,提升用户效率
flutter·华为·交互
雨季6666 小时前
Flutter 三端应用实战:OpenHarmony “心流之泉”——在碎片洪流中,为你筑一眼专注的清泉
开发语言·前端·flutter·交互
一起养小猫6 小时前
Flutter for OpenHarmony 进阶:表达式解析算法与计算器核心实现
算法·flutter·harmonyos