日期
2025-12-27
背景
我更希望底部四个界面(文件、传输、发送、设置)支持左右滑动切换,并且激活的按钮有圆形背景,圆球在切换时能滑动、变色并带有拉伸形变效果。
实现内容
1. PageView 替代 IndexedStack
原来使用 IndexedStack 只能通过点击切换,改为 PageView 支持手势滑动:
body: PageView(
controller: _pageController,
onPageChanged: _onPageChanged,
children: [
FilesPage(onGoToSettings: () => _goToPage(3)),
const TransfersPage(),
const SendPage(),
const SettingsPage(),
],
),2. 自定义底部导航栏
放弃系统 BottomNavigationBar,使用 Stack + Positioned 实现自定义导航栏,以便精确控制圆形指示器的位置和形变。
3. 圆球滑动与变色动画
使用 AnimationController 配合 Tween 实现:
// 位置动画
_positionAnim = Tween<double>(
begin: _prevIndex.toDouble(),
end: _navIndex.toDouble(),
).animate(CurvedAnimation(parent: _animController, curve: Curves.easeOut));
// 颜色动画
_colorAnim = ColorTween(
begin: _navColors[_prevIndex],
end: _navColors[_navIndex],
).animate(CurvedAnimation(parent: _animController, curve: Curves.easeOut));4. 拉伸形变效果(Squash & Stretch)
使用 sin(progress * π) 曲线计算形变量,在动画中间达到最大拉伸:
// 计算形变量:使用 sin 曲线,在动画中间拉伸最大
final progress = animController.value;
final stretch = math.sin(progress * math.pi) * maxStretch; // maxStretch = 20
final currentWidth = circleSize + stretch; // 44 → 64 → 44
// 使用 borderRadius 保持圆角端点
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(circleSize / 2),
),效果:
- 动画开始:圆形(44px)
- 动画中间:最大拉伸成椭圆(64px)
- 动画结束:恢复圆形(44px)
5. 细节修复
- SafeArea:使用
SafeArea(top: false)处理全面屏手势条区域 - 图标居中:图标区域使用
EdgeInsets.only(top: 8)与圆形背景的top: 8保持一致 - 溢出修复:高度从 70 调整为 72,间距和字号微调
关键代码结构
_HomePageState (with SingleTickerProviderStateMixin)├── PageController // 控制页面滑动├── AnimationController // 控制圆球动画├── Animation<double> // 位置动画├── Animation<Color?> // 颜色动画└── _AnimatedBottomNav // 自定义底部导航组件 ├── Stack │ ├── Positioned // 滑动圆形背景(带形变) │ └── Row // 图标和标签 └── SafeArea // 安全区域动画参数
- 动画时长:250ms
- 动画曲线:Curves.easeOut
- 最大拉伸量:20px
- 圆形大小:44px
- 导航栏高度:72px
主题色
| 页面 | 颜色 |
|---|---|
| 文件 | Colors.amber |
| 传输 | Colors.blue[800] |
| 发送 | Colors.green |
| 设置 | Colors.redAccent |
参考资源
- indicator_bottom_navigationbar - 形变指示器参考
- Animated active tab indicator - CustomPaint 实现方案
- Squash & Stretch 动画原理 - 经典动画十二法则之一
涉及文件
lib/ui/home_page.dart- 主页面和底部导航栏实现