Flutter实现卡片旋转滑动平滑效果的无包方案求助
实现平滑卡片滑动旋转效果
核心问题是你当前只在页面切换完成后更新索引,导致旋转角度是突变的。要实现平滑过渡,需要监听PageController的实时页面偏移量,动态计算每个卡片的旋转角度和位置。
修改步骤:
- 移除
onPageChanged的setState逻辑,改为监听PageController的页面滚动偏移 - 基于实时的页面偏移值,动态计算每个卡片的旋转角度和位置偏移
- 通过矩阵变换同时实现旋转和位移,让过渡效果更自然
修改后的完整代码:
import 'package:flutter/material.dart'; import 'package:mawada/cardCollections/widgets/card_play_widget.dart'; import 'package:mawada/constants/app_color.dart'; import 'package:mawada/shared/widgets/app_button.dart'; class CardPlay extends StatefulWidget { const CardPlay({super.key}); @override State<CardPlay> createState() => _CardPlayState(); } class _CardPlayState extends State<CardPlay> { final PageController pageController = PageController(viewportFraction: 0.7); 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( backgroundColor: AppColor.background, appBar: AppBar( actions: [ Padding( padding: const EdgeInsets.only(right: 10.0), child: Image.asset( 'assets/logo2.png', fit: BoxFit.cover, height: 55, ), ) ], ), body: ListView( padding: const EdgeInsets.symmetric(vertical: 30), children: [ // Progress indicator Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(30), boxShadow: [ BoxShadow( offset: const Offset(0, 2), color: Colors.grey.withOpacity(0.5), spreadRadius: 1, blurRadius: 2, ) ], ), child: LinearProgressIndicator( value: 0.2, semanticsLabel: "0.2", backgroundColor: Colors.white, valueColor: const AlwaysStoppedAnimation<Color>( AppColor.primary, ), minHeight: 45, borderRadius: BorderRadius.circular(30), ), ), ), const SizedBox(height: 50), SizedBox( height: 400, child: PageView.builder( controller: pageController, itemCount: 10, itemBuilder: (context, index) { // 计算当前卡片与滚动位置的差值 double difference = index - currentPage; // 限制旋转角度范围,避免过度旋转 double rotationAngle = difference.clamp(-0.1, 0.1); // 计算卡片Y轴偏移,让当前卡片更突出 double translateY = difference.abs() * 30; return Transform( transform: Matrix4.identity() ..rotateZ(rotationAngle) ..translate(0, translateY), alignment: Alignment.center, child: const CardPlayWidget(), ); }, ), ) ], ), bottomNavigationBar: Container( height: 150, padding: const EdgeInsets.symmetric(horizontal: 20), child: Center( child: AppButton( elevation: 0, borderSideColor: AppColor.primary, backgroundColor: AppColor.background, foregroundColor: AppColor.primary, buttonText: "Close", onPressed: () {}, ), ), ), ); } }
关键改动说明:
- 新增
currentPage变量存储实时页面偏移值,通过pageController.addListener实时更新状态 - 在
itemBuilder中,根据卡片索引与currentPage的差值动态计算旋转角度和Y轴偏移,实现滑动过程中的平滑过渡 - 使用
Matrix4同时应用旋转和位移效果,让卡片的动画联动更自然 - 移除了原有的
AnimationController,因为PageController的实时偏移已经可以驱动平滑动画
这样修改后,滑动过程中卡片的旋转和位置会随着手势实时平滑变化,完全符合你想要的效果,且未使用任何第三方包。
内容的提问来源于stack exchange,提问作者Afnan
相关产品推荐
相关产品推荐

