You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Instagram直播点赞动画的JS代码转为Flutter(Dart)实现?

实现Flutter版Instagram直播点赞/爱心动画

我正在开发Flutter应用,对JS/CSS/HTML完全不了解,想要实现类似Instagram直播的点赞/爱心动画(效果为表情从底部弹出,向上移动并逐渐消失,伴随随机的大小、左右偏移和表情类型)。我找到了对应的JS实现逻辑,但自行转换为Dart代码时未能实现动画效果,附上我的初始代码,希望帮忙完成相同效果。


修改后的完整实现代码

import 'package:flutter/material.dart';
import 'dart:math';

void main() {
  runApp(const App());
}

class App extends StatefulWidget {
  const App({Key? key}) : super(key: key);

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

class _AppState extends State<App> {
  final List<int> _likes = [];

  void _removeLike(int id) {
    setState(() {
      _likes.removeWhere((like) => like == id);
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Column(
          children: [
            const Text("Instagram 点赞动画演示"),
            const SizedBox(height: 200),
            Container(
              width: 300,
              height: 400,
              decoration: BoxDecoration(
                border: Border.all(color: Colors.red),
              ),
              child: Stack(
                alignment: Alignment.bottomCenter,
                children: [
                  Positioned(
                    bottom: 20,
                    child: ElevatedButton(
                      onPressed: () {
                        setState(() {
                          _likes.add(DateTime.now().millisecondsSinceEpoch);
                        });
                      },
                      child: const Text("点赞"),
                    ),
                  ),
                  ..._likes.map((id) => Bubble(
                        key: Key(id.toString()),
                        id: id,
                        onAnimationEnd: _removeLike,
                      )),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class Bubble extends StatefulWidget {
  final int id;
  final Function(int) onAnimationEnd;

  const Bubble({
    Key? key,
    required this.id,
    required this.onAnimationEnd,
  }) : super(key: key);

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

class _BubbleState extends State<Bubble> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _moveUpAnimation;
  late Animation<double> _scaleAnimation;
  late Animation<double> _opacityAnimation;
  late double _horizontalOffset;
  late String _emoji;
  late double _size;

  final Random _random = Random();

  @override
  void initState() {
    super.initState();
    // 初始化随机参数
    _size = _randomDouble(20, 40);
    _horizontalOffset = _randomDouble(-100, 100);
    final duration = _randomDouble(2, 4);
    _emoji = _pickRandomEmoji();

    // 创建动画控制器
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: duration.toInt()),
    );

    // 上移动画:从0到-300(向上移动300px)
    _moveUpAnimation = Tween<double>(begin: 0, end: -300).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeOut),
    );

    // 缩放动画:从0到1.2再稍微缩小到1
    _scaleAnimation = TweenSequence([
      TweenSequenceItem(tween: Tween<double>(begin: 0, end: 1.2), weight: 1),
      TweenSequenceItem(tween: Tween<double>(begin: 1.2, end: 1), weight: 2),
    ]).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));

    // 透明度动画:从1逐渐到0
    _opacityAnimation = Tween<double>(begin: 1, end: 0).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeIn),
    );

    // 启动动画,结束后移除组件
    _controller.forward().whenComplete(() {
      widget.onAnimationEnd(widget.id);
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        return Transform.translate(
          offset: Offset(_horizontalOffset, _moveUpAnimation.value),
          child: Transform.scale(
            scale: _scaleAnimation.value,
            child: Opacity(
              opacity: _opacityAnimation.value,
              child: Text(
                _emoji,
                style: TextStyle(fontSize: _size),
              ),
            ),
          ),
        );
      },
    );
  }

  double _randomDouble(double min, double max) {
    return _random.nextDouble() * (max - min) + min;
  }

  String _pickRandomEmoji() {
    const emojis = ["❤️", "👍", "😆", "😮"];
    return emojis[_random.nextInt(emojis.length)];
  }
}

核心实现说明

  • 布局调整:将原有的Column改为Stack,让所有点赞表情在同一层叠加,保留点赞按钮在底部固定位置,还原直播界面的交互逻辑。
  • 多动画组合:
    • 上移动画:通过Tween实现Y轴偏移,让表情从底部向上移动。
    • 缩放动画:用TweenSequence实现先放大再恢复的弹性效果,模拟弹出的真实感。
    • 透明度动画:从1渐变到0,让表情在移动过程中自然消失。
  • 随机参数控制:随机生成表情类型、大小、左右偏移量、动画时长,完全复刻Instagram点赞的随机性。
  • 生命周期管理:动画结束后自动调用父组件方法移除已完成的动画组件,同时在State销毁时释放动画控制器,避免内存泄漏。

内容的提问来源于stack exchange,提问作者iPatel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 12:53:15