152 lines
4.4 KiB
Dart
152 lines
4.4 KiB
Dart
// bezier_bottom_navigation_bar.dart
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_svg/svg.dart';
|
||
import 'package:vbvs_app/common/util/FitTool.dart';
|
||
|
||
class BezierBottomNavigationBar extends StatelessWidget {
|
||
final int selectedIndex;
|
||
final double animatedPosition;
|
||
final ValueChanged<int> onTap;
|
||
final List<String> path;
|
||
final List<String> titles;
|
||
|
||
const BezierBottomNavigationBar({
|
||
Key? key,
|
||
required this.selectedIndex,
|
||
required this.animatedPosition,
|
||
required this.onTap,
|
||
required this.path,
|
||
required this.titles,
|
||
}) : super(key: key);
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final itemCount = path.length;
|
||
return SizedBox(
|
||
height: 130.rpx,
|
||
child: Stack(
|
||
children: [
|
||
Positioned.fill(
|
||
child: CustomPaint(
|
||
size: Size(MediaQuery.of(context).size.width, 130.rpx),
|
||
painter: BezierPainter(
|
||
position: animatedPosition,
|
||
itemCount: itemCount,
|
||
),
|
||
),
|
||
),
|
||
Row(
|
||
children: path.asMap().entries.map((entry) {
|
||
final i = entry.key;
|
||
final icon = entry.value;
|
||
return Expanded(
|
||
child: GestureDetector(
|
||
// 用 InkWell 替换 GestureDetector,支持点击反馈
|
||
onTap: () => onTap(i),
|
||
// splashColor: Colors.transparent, // 可选:关闭 splash 效果
|
||
// highlightColor: Colors.transparent, // 可选:关闭高亮效果
|
||
child: SizedBox.expand(
|
||
child: Padding(
|
||
padding: EdgeInsets.only(top: 14.rpx),
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.start,
|
||
children: [
|
||
Padding(
|
||
padding: EdgeInsets.only(top: 11.rpx),
|
||
child: SvgPicture.asset(icon,
|
||
width: 42.rpx,
|
||
height: 42.rpx,
|
||
color: selectedIndex == i
|
||
? Colors.white
|
||
: Color(0XFF929699)),
|
||
),
|
||
SizedBox(height: 3),
|
||
Text(
|
||
titles[i],
|
||
style: TextStyle(
|
||
fontSize: 22.rpx,
|
||
color: selectedIndex == i
|
||
? Colors.white
|
||
: Colors.grey.shade400,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
)
|
||
// 关键点:撑满 Expanded 区域
|
||
|
||
),
|
||
),
|
||
);
|
||
}).toList(),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
class BezierPainter extends CustomPainter {
|
||
final double position;
|
||
final int itemCount;
|
||
|
||
BezierPainter({required this.position, required this.itemCount});
|
||
|
||
@override
|
||
void paint(Canvas canvas, Size size) {
|
||
final paint = Paint()
|
||
..color = Colors.white
|
||
..style = PaintingStyle.stroke
|
||
..strokeWidth = 1.0;
|
||
|
||
final path = Path();
|
||
final itemWidth = size.width / itemCount;
|
||
final curveWidth = itemWidth * 0.8;
|
||
double curveHeight = 8.5;
|
||
|
||
final centerX = itemWidth * position + itemWidth / 2;
|
||
final left = centerX - curveWidth / 2;
|
||
final right = centerX + curveWidth / 2;
|
||
|
||
// 控制点偏移比例(越小越平滑)
|
||
final controlOffsetX = curveWidth * 0.2;
|
||
final controlOffsetY = curveHeight * 0.9;
|
||
|
||
// 起点
|
||
path.moveTo(0, 0);
|
||
|
||
// 到左侧前的一段直线
|
||
path.lineTo(left, 0);
|
||
|
||
// 凸起贝塞尔曲线
|
||
path.cubicTo(
|
||
left + controlOffsetX,
|
||
0,
|
||
centerX - controlOffsetX,
|
||
-controlOffsetY,
|
||
centerX,
|
||
-controlOffsetY,
|
||
);
|
||
path.cubicTo(
|
||
centerX + controlOffsetX,
|
||
-controlOffsetY,
|
||
right - controlOffsetX,
|
||
0,
|
||
right,
|
||
0,
|
||
);
|
||
|
||
// 右边剩余直线
|
||
path.lineTo(size.width, 0);
|
||
|
||
// 向下平移,保证凸起在容器内部
|
||
canvas.translate(0, controlOffsetY);
|
||
canvas.drawPath(path, paint);
|
||
}
|
||
|
||
@override
|
||
bool shouldRepaint(covariant BezierPainter oldDelegate) {
|
||
return oldDelegate.position != position;
|
||
}
|
||
}
|