如何在Flutter中实现该自定义形状的AppBar?
实现底部弧形AppBar的几种替代方案
你遇到的AppBar和body分隔的问题,主要是因为默认AppBar的阴影(elevation)以及形状裁剪后和body的衔接问题,除了用Stack模拟的方式,还有以下几种更简洁的实现思路:
1. 自定义带弧形的PreferredSize AppBar
直接把AppBar整体裁剪成带弧形的形状,同时去掉阴影,让body顶部和弧形自然衔接:
自定义AppBar代码
class CurvedAppBar extends StatelessWidget implements PreferredSizeWidget { final String title; const CurvedAppBar({super.key, required this.title}); @override Widget build(BuildContext context) { return ClipPath( clipper: _CurvedAppBarClipper(), child: AppBar( title: Text(title), backgroundColor: Colors.blue, elevation: 0, // 移除阴影,避免和body产生分隔感 ), ); } @override Size get preferredSize => const Size.fromHeight(kToolbarHeight); } // 自定义裁剪器,绘制底部弧形 class _CurvedAppBarClipper extends CustomClipper<Path> { @override Path getClip(Size size) { final path = Path(); path.lineTo(0, size.height); // 二次贝塞尔曲线绘制弧形,可调整controlPoint的y值改变弧度大小 final controlPoint = Offset(size.width / 2, size.height + 20); final endPoint = Offset(size.width, size.height); path.quadraticBezierTo(controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy); path.lineTo(size.width, 0); return path; } @override bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false; }
衔接body的处理
为了让body顶部和AppBar的弧形完全贴合,需要在body最顶部添加一个匹配的弧形部件:
Container( color: Colors.white, child: ClipPath( clipper: _CurvedBodyClipper(), child: Container( height: 20, // 和AppBar弧形的高度对应 color: Colors.blue, ), ), ) // body顶部的弧形裁剪器 class _CurvedBodyClipper extends CustomClipper<Path> { @override Path getClip(Size size) { final path = Path(); path.lineTo(0, 0); final controlPoint = Offset(size.width / 2, -20); final endPoint = Offset(size.width, 0); path.quadraticBezierTo(controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy); path.lineTo(size.width, size.height); path.lineTo(0, size.height); return path; } @override bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false; }
2. 利用extendBodyBehindAppBar实现背景延伸
让AppBar透明,然后在body的顶部用自定义绘制实现弧形背景,同时保留AppBar的导航元素:
Scaffold( extendBodyBehindAppBar: true, // 让body延伸到AppBar下方 appBar: AppBar( title: const Text("弧形AppBar"), backgroundColor: Colors.transparent, elevation: 0, ), body: Stack( children: [ // 顶部弧形背景,高度包含AppBar和弧形区域 CustomPaint( painter: _CurvedBackgroundPainter(), child: SizedBox(height: kToolbarHeight + 20), ), // 主体内容,设置padding避开顶部弧形区域 Padding( padding: EdgeInsets.only(top: kToolbarHeight + 20), child: Container( color: Colors.white, child: const Center(child: Text("页面内容")), ), ), ], ), ) // 绘制弧形背景的Painter class _CurvedBackgroundPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint()..color = Colors.blue; final path = Path(); path.lineTo(0, size.height); final controlPoint = Offset(size.width / 2, size.height + 20); final endPoint = Offset(size.width, size.height); path.quadraticBezierTo(controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy); path.lineTo(size.width, 0); path.close(); canvas.drawPath(path, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; }
这种方式的优势是不需要修改AppBar的结构,只需要处理背景,适合需要背景色渐变或者复杂效果的场景。
3. 给AppBar添加弧形底部组件
利用AppBar的bottom属性,添加一个自定义的弧形部件,让它和AppBar主体颜色一致,看起来像是整体的一部分:
AppBar( title: const Text("弧形AppBar"), backgroundColor: Colors.blue, elevation: 0, bottom: PreferredSize( preferredSize: const Size.fromHeight(20), // 弧形区域的高度 child: ClipPath( clipper: _CurvedBottomClipper(), child: Container( height: 20, color: Colors.blue, // 和AppBar颜色保持一致 ), ), ), ) // 底部弧形裁剪器 class _CurvedBottomClipper extends CustomClipper<Path> { @override Path getClip(Size size) { final path = Path(); path.lineTo(0, size.height); final controlPoint = Offset(size.width / 2, -10); final endPoint = Offset(size.width, size.height); path.quadraticBezierTo(controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy); path.lineTo(size.width, 0); return path; } @override bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false; }
这种方法实现最简单,不需要修改body的结构,只需要给AppBar添加bottom组件即可,适合快速实现需求。
内容的提问来源于stack exchange,提问作者Yusuf Raza
相关产品推荐
相关产品推荐

