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

在Flutter中实现类似Headspace应用的动画效果

Flutter实现Headspace风格页面切换动画

核心实现思路

  • 通过PageController监听页面切换的偏移量,实时计算动画参数
  • 对页面内的核心元素(如卡片)应用缩放、垂直位移和透明度渐变动画
  • 配合曲线动画优化过渡的自然度

完整代码示例

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const PageSwitchAnimation(),
      theme: ThemeData(primarySwatch: Colors.blue),
    );
  }
}

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

  @override
  State<PageSwitchAnimation> createState() => _PageSwitchAnimationState();
}

class _PageSwitchAnimationState extends State<PageSwitchAnimation> {
  final PageController _pageController = PageController(initialPage: 0);
  double _currentPage = 0.0;

  @override
  void initState() {
    super.initState();
    _pageController.addListener(() {
      setState(() {
        _currentPage = _pageController.page!;
      });
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView.builder(
        controller: _pageController,
        itemCount: 3,
        itemBuilder: (context, index) {
          // 计算当前页面与目标页面的偏移差值
          final double difference = _currentPage - index;
          final double opacity = 1 - difference.abs();
          final double scale = 0.8 + (opacity * 0.2);
          final double translateY = difference.abs() * 100;

          return Transform(
            transform: Matrix4.identity()
              ..scale(scale)
              ..translate(0, translateY),
            alignment: Alignment.center,
            child: Opacity(
              opacity: opacity,
              child: _buildPageContent(index),
            ),
          );
        },
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentPage.round(),
        onTap: (index) {
          _pageController.animateToPage(
            index,
            duration: const Duration(milliseconds: 500),
            curve: Curves.easeInOut,
          );
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"),
          BottomNavigationBarItem(icon: Icon(Icons.book), label: "冥想"),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: "我的"),
        ],
      ),
    );
  }

  Widget _buildPageContent(int index) {
    final List<Color> colors = [Colors.blue[100]!, Colors.green[100]!, Colors.purple[100]!];
    final List<String> titles = ["每日冥想", "专注训练", "个人中心"];

    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 80),
      decoration: BoxDecoration(
        color: colors[index],
        borderRadius: BorderRadius.circular(20),
        boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 10, offset: Offset(0, 5))],
      ),
      child: Center(
        child: Text(
          titles[index],
          style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
        ),
      ),
    );
  }
}

关键代码解释

  • 页面监听:通过_pageController.addListener实时获取当前页面的偏移值_currentPage,驱动动画更新
  • 动画参数计算:
    • opacity:根据页面偏移差值的绝对值计算,实现页面切换时的淡入淡出效果
    • scale:基于透明度值动态调整元素缩放比例,未激活页面缩小至80%,激活页面恢复至100%
    • translateY:根据偏移差值让未激活页面向下偏移,模拟Headspace的卡片升起效果
  • 动画执行:使用Transform组合缩放和位移变换,配合Opacity实现多维度动画叠加

优化建议

  • 可调整translateY的数值(当前为100),控制页面偏移幅度
  • 替换Curves.easeInOut为Curves.decelerate等曲线,优化过渡节奏
  • 若需背景色同步过渡,可在Scaffold的backgroundColor中添加基于_currentPage的颜色插值动画

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:32:55