157 lines
4.6 KiB
Dart
157 lines
4.6 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 Material(
|
|
// ✅ 整个底部栏使用一个透明 Material 包裹
|
|
type: MaterialType.transparency,
|
|
child: 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: InkWell(
|
|
onTap: () => onTap(i),
|
|
splashColor: Colors.transparent,
|
|
highlightColor: Colors.transparent,
|
|
focusColor: Colors.transparent,
|
|
hoverColor: 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
|
|
: const Color(0XFF929699),
|
|
),
|
|
),
|
|
const SizedBox(height: 3),
|
|
Text(
|
|
titles[i],
|
|
style: TextStyle(
|
|
fontSize: 22.rpx,
|
|
color: selectedIndex == i
|
|
? Colors.white
|
|
: Colors.grey.shade400,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}).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;
|
|
}
|
|
}
|