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

如何使用Flutter实现iOS Control Center弹窗容器展开动画

Flutter实现弹窗内容器展开动画(类似iOS控制中心音乐播放器)

核心实现思路

通过AnimationController控制动画时长与状态,结合Transform.scale或AnimatedContainer实现容器尺寸的平滑过渡,搭配弹性曲线模拟原生系统的灵动效果,同时可叠加透明度动画增强视觉层次。

完整示例代码

import 'package:flutter/material.dart';

void main() => runApp(const ExpandablePopupDemo());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.grey[900],
        body: const Center(child: ExpandablePlayerCard()),
      ),
    );
  }
}

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

  @override
  State<ExpandablePlayerCard> createState() => _ExpandablePlayerCardState();
}

class _ExpandablePlayerCardState extends State<ExpandablePlayerCard> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _scaleAnimation;
  late Animation<double> _opacityAnimation;
  bool _isExpanded = false;

  @override
  void initState() {
    super.initState();
    // 初始化动画控制器,时长匹配iOS原生动画节奏
    _controller = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    );

    // 缩放动画:从原始尺寸放大到2.5倍
    _scaleAnimation = Tween<double>(begin: 1.0, end: 2.5).animate(
      CurvedAnimation(parent: _controller, curve: Curves.elasticOut),
    );

    // 透明度动画:收起时半透明,展开后完全显示
    _opacityAnimation = Tween<double>(begin: 0.8, end: 1.0).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    );
  }

  void _toggleExpand() {
    setState(() {
      _isExpanded = !_isExpanded;
      _isExpanded ? _controller.forward() : _controller.reverse();
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _toggleExpand,
      child: AnimatedBuilder(
        animation: _controller,
        builder: (context, child) {
          return Transform.scale(
            scale: _scaleAnimation.value,
            child: Opacity(
              opacity: _opacityAnimation.value,
              child: Container(
                width: 200,
                height: 120,
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Colors.grey[800],
                  borderRadius: BorderRadius.circular(16),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withOpacity(0.3),
                      blurRadius: 10,
                      offset: const Offset(0, 5),
                    )
                  ],
                ),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    const Icon(Icons.music_note, color: Colors.white, size: 32),
                    const SizedBox(height: 8),
                    Text(
                      _isExpanded ? "正在播放:Flutter BGM" : "点击展开",
                      style: const TextStyle(color: Colors.white, fontSize: 14),
                      textAlign: TextAlign.center,
                    ),
                    // 展开后显示播放控制按钮
                    if (_isExpanded)
                      const Padding(
                        padding: EdgeInsets.only(top: 16),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: [
                            Icon(Icons.skip_previous, color: Colors.white),
                            Icon(Icons.pause_circle, color: Colors.white, size: 32),
                            Icon(Icons.skip_next, color: Colors.white),
                          ],
                        ),
                      )
                  ],
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

关键细节说明

  • 动画曲线选择:示例使用Curves.elasticOut实现弹性回弹效果,和iOS控制中心的动画质感匹配;偏好平缓效果可替换为Curves.easeInOut。
  • 尺寸控制方案:除了Transform.scale,也可以用AnimatedContainer直接修改width和height属性,两种方式都能实现展开/收起效果。
  • 背景模糊优化:要模拟iOS的背景模糊,可在容器外层嵌套BackdropFilter,搭配ImageFilter.blur(sigmaX: 5, sigmaY: 5)实现。
  • 状态切换逻辑:通过_isExpanded变量控制状态,点击时触发动画控制器的forward()(展开)或reverse()(收起)方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 08:53:22