在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
相关产品推荐
相关产品推荐

