如何在Flutter中实现PlayStation风格的底部TabBar?
实现PlayStation风格的Flutter底部TabBar
我希望在Flutter中实现PlayStation风格的底部TabBar,目前已编写了如下CustomClipper代码:
import 'package:flutter/material.dart'; class BottomBarClipper extends CustomClipper<Path> { @override Path getClip(Size size) { Path path = Path(); final notchRadius = 25.0; path.lineTo(size.width / 2 - notchRadius * 2, 0); path.relativeArcToPoint( Offset(notchRadius * 2, notchRadius), radius: Radius.circular(notchRadius), clockwise: false, ); path.lineTo(size.width / 2 + notchRadius * 2, 0); path.lineTo(size.width, 0); path.lineTo(size.width, size.height); path.lineTo(0, size.height); path.close(); return path; } @override bool shouldReclip(CustomClipper<Path> oldClipper) => false; }
但我不确定如何修改这个CustomClipper,以实现中间按钮向上凸起的PlayStation风格底部TabBar效果。
修改后的CustomClipper(实现凸起效果)
要实现中间向上凸起的形状,需要调整路径绘制逻辑,把原来的凹口改成向外凸起的弧度:
import 'package:flutter/material.dart'; class PSBottomBarClipper extends CustomClipper<Path> { final double notchRadius; PSBottomBarClipper({this.notchRadius = 30.0}); @override Path getClip(Size size) { final path = Path(); final centerX = size.width / 2; final notchDiameter = notchRadius * 2; // 从左下角开始绘制路径 path.moveTo(0, size.height); // 画到凸起左侧的起点 path.lineTo(centerX - notchDiameter, size.height); // 绘制向上凸起的圆弧 path.arcToPoint( Offset(centerX + notchDiameter, size.height), radius: Radius.circular(notchRadius), clockwise: false, ); // 画到右下角 path.lineTo(size.width, size.height); // 画到右上角 path.lineTo(size.width, 0); // 画到左上角 path.lineTo(0, 0); // 闭合路径 path.close(); return path; } @override bool shouldReclip(CustomClipper<Path> oldClipper) => oldClipper is PSBottomBarClipper && oldClipper.notchRadius != notchRadius; }
如何在底部TabBar中使用
可以通过ClipPath包裹底部导航栏的容器,配合悬浮按钮实现完整的PlayStation风格效果,以下是完整示例:
class PSBottomTabBar extends StatelessWidget { final int selectedIndex; final ValueChanged<int> onItemTapped; const PSBottomTabBar({ super.key, required this.selectedIndex, required this.onItemTapped, }); @override Widget build(BuildContext context) { final barHeight = 60.0; final notchRadius = 30.0; return ClipPath( clipper: PSBottomBarClipper(notchRadius: notchRadius), child: Container( height: barHeight, color: Colors.black, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildTabItem(Icons.home, 0), _buildTabItem(Icons.games, 1), // 中间留空,适配悬浮按钮位置 SizedBox(width: notchRadius * 2), _buildTabItem(Icons.person, 2), _buildTabItem(Icons.settings, 3), ], ), ), ); } Widget _buildTabItem(IconData icon, int index) { final isSelected = selectedIndex == index; return IconButton( icon: Icon( icon, color: isSelected ? Colors.blueAccent : Colors.grey, size: 28, ), onPressed: () => onItemTapped(index), ); } } // 在Scaffold中集成使用 class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { int _selectedIndex = 0; void _onItemTapped(int index) { setState(() { _selectedIndex = index; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("PS风格底部导航")), body: const Center(child: Text("页面内容")), floatingActionButton: FloatingActionButton( onPressed: () {}, child: const Icon(Icons.play_arrow), backgroundColor: Colors.blueAccent, ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, bottomNavigationBar: PSBottomTabBar( selectedIndex: _selectedIndex, onItemTapped: _onItemTapped, ), ); } }
关键说明
- 修改后的
PSBottomBarClipper从底部开始绘制路径,通过arcToPoint创建向上凸起的圆弧,完美匹配PlayStation风格的底部栏形状。 - 使用
ClipPath将容器裁剪为目标形状,内部放置Tab按钮,中间留空位置配合FloatingActionButton实现凸起按钮的效果。 - 可通过调整
notchRadius参数控制凸起大小,适配不同设计需求。
内容的提问来源于stack exchange,提问作者conjurers 22
相关产品推荐
相关产品推荐

