feat: 底部TabBar优化

This commit is contained in:
chaos-zhu
2026-05-23 16:45:43 +08:00
parent 8dd6c34b2c
commit ede37a131c
2 changed files with 1360 additions and 1660 deletions
+1251 -1620
View File
File diff suppressed because it is too large Load Diff
+109 -40
View File
@@ -1,3 +1,5 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -38,7 +40,9 @@ class _MainShellPageState extends ConsumerState<MainShellPage> {
final l = AppLocalizations.of(context);
return Scaffold(
backgroundColor: const Color(0xFFF7EFE0),
body: SafeArea(child: IndexedStack(index: _index, children: _tabs)),
body: SafeArea(
child: IndexedStack(index: _index, children: _tabs),
),
bottomNavigationBar: _WarmBottomBar(
selectedIndex: _index,
onSelected: (i) => setState(() => _index = i),
@@ -83,30 +87,91 @@ class _WarmBottomBar extends StatelessWidget {
child: Container(
color: const Color(0xFFF7EFE0),
padding: const EdgeInsets.fromLTRB(21, 12, 21, 21),
child: Container(
height: 62,
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: const Color(0xFFFBF5E6),
borderRadius: BorderRadius.circular(36),
border: Border.all(color: const Color(0xFFE2D5B3)),
),
child: Row(
children: [
for (var i = 0; i < items.length; i++)
Expanded(
child: _WarmBottomBarButton(
item: items[i],
selected: i == selectedIndex,
onTap: () => onSelected(i),
child: ClipRRect(
borderRadius: BorderRadius.circular(36),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 18, sigmaY: 18),
child: Container(
height: 62,
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: const Color(0xFFFBF5E6).withValues(alpha: 0.68),
borderRadius: BorderRadius.circular(36),
border: Border.all(color: Colors.white.withValues(alpha: 0.52)),
boxShadow: [
BoxShadow(
color: const Color(0xFF5C4520).withValues(alpha: 0.08),
blurRadius: 20,
offset: const Offset(0, 8),
),
),
],
],
),
child: Stack(
children: [
AnimatedAlign(
duration: const Duration(milliseconds: 260),
curve: Curves.easeOutCubic,
alignment: _indicatorAlignment,
child: FractionallySizedBox(
widthFactor: 1 / items.length,
heightFactor: 1,
child: const _WarmBottomBarIndicator(),
),
),
Row(
children: [
for (var i = 0; i < items.length; i++)
Expanded(
child: _WarmBottomBarButton(
item: items[i],
selected: i == selectedIndex,
onTap: () => onSelected(i),
),
),
],
),
],
),
),
),
),
),
);
}
Alignment get _indicatorAlignment {
if (items.length <= 1) return Alignment.center;
final step = 2 / (items.length - 1);
return Alignment(-1 + (step * selectedIndex), 0);
}
}
class _WarmBottomBarIndicator extends StatelessWidget {
const _WarmBottomBarIndicator();
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 2),
decoration: BoxDecoration(
color: const Color(0xFFE5B33A).withValues(alpha: 0.74),
borderRadius: BorderRadius.circular(28),
border: Border.all(color: Colors.white.withValues(alpha: 0.38)),
boxShadow: [
BoxShadow(
color: const Color(0xFFE5B33A).withValues(alpha: 0.24),
blurRadius: 16,
offset: const Offset(0, 6),
),
BoxShadow(
color: Colors.white.withValues(alpha: 0.18),
blurRadius: 8,
offset: const Offset(0, -2),
),
],
),
);
}
}
class _WarmBottomBarButton extends StatelessWidget {
@@ -126,28 +191,35 @@ class _WarmBottomBarButton extends StatelessWidget {
return Tooltip(
message: item.label,
child: Material(
color: selected ? const Color(0xFFE5B33A) : Colors.transparent,
color: Colors.transparent,
borderRadius: BorderRadius.circular(26),
child: InkWell(
borderRadius: BorderRadius.circular(26),
onTap: onTap,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(item.icon, size: 20, color: color),
const SizedBox(height: 4),
Text(
item.label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: color,
fontSize: 10,
fontWeight: FontWeight.w700,
letterSpacing: 0.5,
),
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 180),
curve: Curves.easeOut,
style: TextStyle(
color: color,
fontSize: 10,
fontWeight: FontWeight.w700,
letterSpacing: 0.5,
),
child: IconTheme(
data: IconThemeData(size: 20, color: color),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(item.icon),
const SizedBox(height: 4),
Text(
item.label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
],
),
),
),
),
@@ -156,10 +228,7 @@ class _WarmBottomBarButton extends StatelessWidget {
}
class _WarmBottomBarItem {
const _WarmBottomBarItem({
required this.icon,
required this.label,
});
const _WarmBottomBarItem({required this.icon, required this.label});
final IconData icon;
final String label;