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浮动按钮在OpenHarmony平台的实践经验
flutter·harmonyos·鸿蒙
程序员老刘17 小时前
一杯奶茶钱,PicGo + 阿里云 OSS 搭建永久稳定的个人图床
flutter·markdown
奋斗的小青年!!20 小时前
OpenHarmony Flutter 拖拽排序组件性能优化与跨平台适配指南
flutter·harmonyos·鸿蒙
小雨下雨的雨21 小时前
Flutter 框架跨平台鸿蒙开发 —— Stack 控件之三维层叠艺术
flutter·华为·harmonyos
行者961 天前
OpenHarmony平台Flutter手风琴菜单组件的跨平台适配实践
flutter·harmonyos·鸿蒙
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Flex 控件之响应式弹性布局
flutter·ui·华为·harmonyos·鸿蒙系统
cn_mengbei1 天前
Flutter for OpenHarmony 实战:CheckboxListTile 复选框列表项详解
flutter
cn_mengbei1 天前
Flutter for OpenHarmony 实战:Switch 开关按钮详解
flutter
奋斗的小青年!!1 天前
OpenHarmony Flutter实战:打造高性能订单确认流程步骤条
flutter·harmonyos·鸿蒙