2026-04-02 18:39:35 +08:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
|
|
|
|
|
import '../../l10n/app_localizations.dart';
|
|
|
|
|
|
|
|
|
|
import '../theme/design_tokens.dart';
|
|
|
|
|
|
|
|
|
|
enum MainTab { home, profile }
|
|
|
|
|
|
|
|
|
|
class BottomNavBar extends StatelessWidget {
|
|
|
|
|
const BottomNavBar({
|
|
|
|
|
super.key,
|
|
|
|
|
required this.currentTab,
|
|
|
|
|
required this.onTabChange,
|
|
|
|
|
required this.onLogoTap,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
final MainTab currentTab;
|
|
|
|
|
final ValueChanged<MainTab> onTabChange;
|
|
|
|
|
final VoidCallback onLogoTap;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
final l10n = AppLocalizations.of(context)!;
|
|
|
|
|
final colors = Theme.of(context).colorScheme;
|
|
|
|
|
|
|
|
|
|
return Container(
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: AppSpacing.sm),
|
|
|
|
|
color: colors.surface,
|
|
|
|
|
child: SafeArea(
|
|
|
|
|
top: false,
|
|
|
|
|
child: Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
|
|
|
children: [
|
|
|
|
|
_NavItem(
|
|
|
|
|
icon: Icons.home,
|
|
|
|
|
label: l10n.homeTab,
|
|
|
|
|
selected: currentTab == MainTab.home,
|
|
|
|
|
onTap: () => onTabChange(MainTab.home),
|
|
|
|
|
),
|
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: onLogoTap,
|
|
|
|
|
child: ClipRRect(
|
|
|
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
|
|
|
child: Image.asset(
|
|
|
|
|
'assets/images/logo.png',
|
|
|
|
|
width: 56,
|
|
|
|
|
height: 56,
|
|
|
|
|
fit: BoxFit.cover,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
_NavItem(
|
|
|
|
|
icon: Icons.person,
|
|
|
|
|
label: l10n.profileTab,
|
|
|
|
|
selected: currentTab == MainTab.profile,
|
|
|
|
|
onTap: () => onTabChange(MainTab.profile),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _NavItem extends StatelessWidget {
|
|
|
|
|
const _NavItem({
|
|
|
|
|
required this.icon,
|
|
|
|
|
required this.label,
|
|
|
|
|
required this.selected,
|
|
|
|
|
required this.onTap,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
final IconData icon;
|
|
|
|
|
final String label;
|
|
|
|
|
final bool selected;
|
|
|
|
|
final VoidCallback onTap;
|
|
|
|
|
|
2026-04-07 18:43:58 +08:00
|
|
|
IconData get _filledIcon {
|
|
|
|
|
switch (icon) {
|
|
|
|
|
case Icons.home:
|
|
|
|
|
return Icons.home;
|
|
|
|
|
case Icons.person:
|
|
|
|
|
return Icons.person;
|
|
|
|
|
default:
|
|
|
|
|
return icon;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IconData get _outlinedIcon {
|
|
|
|
|
switch (icon) {
|
|
|
|
|
case Icons.home:
|
|
|
|
|
return Icons.home_outlined;
|
|
|
|
|
case Icons.person:
|
|
|
|
|
return Icons.person_outline;
|
|
|
|
|
default:
|
|
|
|
|
return icon;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 18:39:35 +08:00
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
final colors = Theme.of(context).colorScheme;
|
|
|
|
|
return InkWell(
|
|
|
|
|
onTap: onTap,
|
|
|
|
|
borderRadius: BorderRadius.circular(AppRadius.md),
|
|
|
|
|
child: Padding(
|
|
|
|
|
padding: const EdgeInsets.all(AppSpacing.sm),
|
|
|
|
|
child: Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
children: [
|
2026-04-07 18:43:58 +08:00
|
|
|
Icon(selected ? _filledIcon : _outlinedIcon, color: colors.primary),
|
2026-04-02 18:39:35 +08:00
|
|
|
const SizedBox(height: AppSpacing.xs),
|
|
|
|
|
Text(
|
|
|
|
|
label,
|
2026-04-03 16:56:47 +08:00
|
|
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
2026-04-07 18:43:58 +08:00
|
|
|
color: colors.primary,
|
2026-04-03 16:56:47 +08:00
|
|
|
fontWeight: selected ? FontWeight.w600 : FontWeight.w500,
|
|
|
|
|
),
|
2026-04-02 18:39:35 +08:00
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|