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

Flutter:如何避免ChangeNotifier的notifyListeners阻断按钮动画?

解决方案

核心思路是拆分状态管理:把全局点赞状态(是否点赞、计数)交给ChangeNotifier管理,而每个按钮的动画状态(播放状态、控制器)由按钮自身的StatefulWidget独立持有,避免notifyListeners触发的重建打断动画。

步骤1:定义全局状态Notifier

class LikeNotifier extends ChangeNotifier {
  bool _isLiked = false;
  int _likeCount = 0;

  bool get isLiked => _isLiked;
  int get likeCount => _likeCount;

  void toggleLike() {
    _isLiked = !_isLiked;
    _likeCount += _isLiked ? 1 : -1;
    notifyListeners();
  }
}

步骤2:实现带独立动画的点赞按钮

将动画控制器放在按钮的State中,仅用Consumer包裹需要同步的静态UI部分(图标、计数),动画相关组件不受Consumer重建影响:

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

  @override
  State<LikeButton> createState() => _LikeButtonState();
}

class _LikeButtonState extends State<LikeButton> with SingleTickerProviderStateMixin {
  late AnimationController _animationController;
  late Animation<double> _scaleAnimation;

  @override
  void initState() {
    super.initState();
    // 初始化动画控制器和缩放动画
    _animationController = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    );
    _scaleAnimation = Tween<double>(begin: 1.0, end: 1.3).animate(
      CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
    );
  }

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

  // 播放点赞弹出动画
  Future<void> _playLikeAnimation() async {
    await _animationController.forward();
    await _animationController.reverse();
  }

  @override
  Widget build(BuildContext context) {
    // 获取全局状态(listen: false 避免不必要重建)
    final likeNotifier = Provider.of<LikeNotifier>(context, listen: false);

    return GestureDetector(
      onTap: () async {
        // 先播放当前按钮的动画,再更新全局状态
        if (!likeNotifier.isLiked) {
          await _playLikeAnimation();
        }
        likeNotifier.toggleLike();
      },
      child: ScaleTransition(
        scale: _scaleAnimation,
        // 仅用Consumer包裹需要同步的UI(图标、计数)
        child: Consumer<LikeNotifier>(
          builder: (context, notifier, child) {
            return Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(
                  notifier.isLiked ? Icons.favorite : Icons.favorite_border,
                  color: notifier.isLiked ? Colors.red : Colors.grey,
                  size: 32,
                ),
                const SizedBox(height: 4),
                Text('${notifier.likeCount}'),
              ],
            );
          },
        ),
      ),
    );
  }
}

步骤3:在页面中使用按钮

通过ChangeNotifierProvider注入全局状态,多个按钮自动同步:

class LikePage extends StatelessWidget {
  const LikePage({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => LikeNotifier(),
      child: Scaffold(
        appBar: AppBar(title: const Text('点赞同步示例')),
        body: const Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              LikeButton(),
              SizedBox(height: 60),
              LikeButton(),
            ],
          ),
        ),
      ),
    );
  }
}

原理说明

  1. 动画独立持有:每个LikeButton的State中保存自己的AnimationController,不会因Consumer重建而被销毁或重置,动画能完整播放。
  2. 局部更新UI:仅用Consumer包裹需要同步的图标和计数,全局状态变化时只有这部分UI重建,不影响动画组件。
  3. 状态同步保证:调用likeNotifier.toggleLike()后触发notifyListeners(),所有按钮的Consumer都会更新静态UI,实现多按钮状态统一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 01:15:55