Flutter中如何为带CircularNotchedRectangle的BottomAppBar加圆角并保阴影
解决方案:带圆角、缺口且保留阴影的BottomAppBar
嘿,这个问题我太熟悉了!用ClipRRect包裹BottomAppBar确实会丢失阴影——因为Material组件的阴影是绘制在自身边界之外的,ClipRRect会把边界外的内容都裁剪掉,自然就看不到阴影了。
给你两个实用的解决方案,都能实现带圆角、圆形缺口且保留Material阴影的BottomAppBar:
方案一:用外层Material组件提供阴影和圆角(推荐)
这个方法最简洁,不需要自定义形状,只需要把BottomAppBar放在一个带圆角和阴影的Material组件内部,同时关闭BottomAppBar自身的阴影即可:
class BottomNavigationView extends StatelessWidget { @override Widget build(BuildContext context) { return Transform.translate( offset: Offset(0.0, -10), child: Container( margin: EdgeInsets.only(left: 20, right: 20), // 外层Material负责提供阴影和圆角 child: Material( elevation: 8.0, // 调整阴影强度,和原BottomAppBar一致即可 borderRadius: BorderRadius.all(Radius.circular(30)), child: BottomAppBar( elevation: 0.0, // 关闭BottomAppBar自身的阴影 shape: CircularNotchedRectangle(), // 保留圆形缺口 child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Icon(Icons.accessibility_new), ], ), _buildMiddleTabItem(), Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Icon(Icons.accessibility_new), ], ) ], ), ), ), ), ); } Widget _buildMiddleTabItem() { return Expanded( child: SizedBox( height: 60, child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ SizedBox(height: 24), Text(''), ], ), ), ); } }
原理说明:
- 外层
Material组件负责绘制圆角和阴影,它的边界是带圆角的,阴影会正确显示在圆角边界外 BottomAppBar只负责提供圆形缺口的形状,关闭自身的elevation避免重复阴影- 这种方法不需要修改原有
BottomAppBar的结构,代码改动极小
方案二:自定义Shape同时实现圆角和缺口
如果希望BottomAppBar自身就具备圆角+缺口的形状,可以自定义一个NotchedShape,把CircularNotchedRectangle的缺口逻辑和圆角结合:
首先定义自定义形状:
class RoundCornerNotchedShape extends NotchedShape { final BorderRadius borderRadius; final CircularNotchedRectangle _innerNotch = CircularNotchedRectangle(); RoundCornerNotchedShape({required this.borderRadius}); @override Path getOuterPath(Rect host, Rect? guest) { // 先获取圆形缺口的路径 final notchPath = _innerNotch.getOuterPath(host, guest); // 再创建一个带圆角的矩形路径 final roundedRectPath = Path() ..addRRect(RRect.fromRectAndCorners( host, topLeft: borderRadius.topLeft, topRight: borderRadius.topRight, bottomLeft: borderRadius.bottomLeft, bottomRight: borderRadius.bottomRight, )); // 合并两个路径,取交集就是带圆角和缺口的形状 return Path.combine(PathOperation.intersect, notchPath, roundedRectPath); } }
然后修改BottomNavigationView使用这个自定义形状:
class BottomNavigationView extends StatelessWidget { @override Widget build(BuildContext context) { return Transform.translate( offset: Offset(0.0, -10), child: Container( margin: EdgeInsets.only(left: 20, right: 20), child: BottomAppBar( elevation: 8.0, // 保留自身阴影 // 使用自定义的圆角+缺口形状 shape: RoundCornerNotchedShape( borderRadius: BorderRadius.all(Radius.circular(30)), ), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Icon(Icons.accessibility_new), ], ), _buildMiddleTabItem(), Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Icon(Icons.accessibility_new), ], ) ], ), ), ), ); } Widget _buildMiddleTabItem() { return Expanded( child: SizedBox( height: 60, child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ SizedBox(height: 24), Text(''), ], ), ), ); } }
原理说明:
- 自定义
RoundCornerNotchedShape同时实现了圆形缺口和圆角的路径计算 BottomAppBar使用这个形状后,自身的边界就是带圆角和缺口的,阴影会正确绘制在这个边界外,不需要任何裁剪
两种方案都能完美解决你的问题,个人更推荐方案一,代码更简洁易维护。
内容的提问来源于stack exchange,提问作者Da Artagnan
相关产品推荐
相关产品推荐

