Files
unionflow-server-impl-quarkus/unionflow-mobile-apps/lib/features/members/presentation/widgets/modern_tab_bar.dart
2025-09-13 19:05:06 +00:00

206 lines
5.8 KiB
Dart

import 'package:flutter/material.dart';
import '../../../../shared/theme/app_theme.dart';
import '../../../../shared/theme/design_system.dart';
/// TabBar moderne avec animations et design professionnel
class ModernTabBar extends StatefulWidget implements PreferredSizeWidget {
const ModernTabBar({
super.key,
required this.controller,
required this.tabs,
this.onTap,
});
final TabController controller;
final List<ModernTab> tabs;
final ValueChanged<int>? onTap;
@override
State<ModernTabBar> createState() => _ModernTabBarState();
@override
Size get preferredSize => Size.fromHeight(DesignSystem.goldenWidth(60));
}
class _ModernTabBarState extends State<ModernTabBar>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: DesignSystem.animationFast,
vsync: this,
);
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 0.95,
).animate(CurvedAnimation(
parent: _animationController,
curve: DesignSystem.animationCurve,
));
widget.controller.addListener(_onTabChanged);
}
@override
void dispose() {
widget.controller.removeListener(_onTabChanged);
_animationController.dispose();
super.dispose();
}
void _onTabChanged() {
if (mounted) {
_animationController.forward().then((_) {
_animationController.reverse();
});
}
}
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(
horizontal: DesignSystem.spacingLg,
vertical: DesignSystem.spacingSm,
),
decoration: BoxDecoration(
color: AppTheme.surfaceLight,
borderRadius: BorderRadius.circular(DesignSystem.radiusLg),
boxShadow: DesignSystem.shadowCard,
border: Border.all(
color: AppTheme.borderColor.withOpacity(0.1),
width: 1,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(DesignSystem.radiusLg),
child: TabBar(
controller: widget.controller,
onTap: widget.onTap,
indicator: BoxDecoration(
gradient: DesignSystem.primaryGradient,
borderRadius: BorderRadius.circular(DesignSystem.radiusMd),
),
indicatorSize: TabBarIndicatorSize.tab,
indicatorPadding: EdgeInsets.all(DesignSystem.spacingXs),
labelColor: Colors.white,
unselectedLabelColor: AppTheme.textSecondary,
labelStyle: DesignSystem.labelLarge.copyWith(
fontWeight: FontWeight.w600,
),
unselectedLabelStyle: DesignSystem.labelLarge.copyWith(
fontWeight: FontWeight.w500,
),
dividerColor: Colors.transparent,
tabs: widget.tabs.asMap().entries.map((entry) {
final index = entry.key;
final tab = entry.value;
final isSelected = widget.controller.index == index;
return AnimatedBuilder(
animation: _scaleAnimation,
builder: (context, child) {
return Transform.scale(
scale: isSelected ? _scaleAnimation.value : 1.0,
child: _buildTab(tab, isSelected),
);
},
);
}).toList(),
),
),
);
}
Widget _buildTab(ModernTab tab, bool isSelected) {
return Container(
height: DesignSystem.goldenWidth(50),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedContainer(
duration: DesignSystem.animationFast,
child: Icon(
tab.icon,
size: isSelected ? 20 : 18,
color: isSelected ? Colors.white : AppTheme.textSecondary,
),
),
if (tab.label != null) ...[
SizedBox(width: DesignSystem.spacingXs),
AnimatedDefaultTextStyle(
duration: DesignSystem.animationFast,
style: (isSelected ? DesignSystem.labelLarge : DesignSystem.labelMedium).copyWith(
color: isSelected ? Colors.white : AppTheme.textSecondary,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500,
),
child: Text(tab.label!),
),
],
if (tab.badge != null) ...[
SizedBox(width: DesignSystem.spacingXs),
_buildBadge(tab.badge!, isSelected),
],
],
),
);
}
Widget _buildBadge(String badge, bool isSelected) {
return AnimatedContainer(
duration: DesignSystem.animationFast,
padding: EdgeInsets.symmetric(
horizontal: DesignSystem.spacingXs,
vertical: 2,
),
decoration: BoxDecoration(
color: isSelected
? Colors.white.withOpacity(0.2)
: AppTheme.primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(DesignSystem.radiusSm),
),
child: Text(
badge,
style: DesignSystem.labelSmall.copyWith(
color: isSelected ? Colors.white : AppTheme.primaryColor,
fontWeight: FontWeight.w600,
fontSize: 10,
),
),
);
}
}
/// Modèle pour un onglet moderne
class ModernTab {
const ModernTab({
required this.icon,
this.label,
this.badge,
});
final IconData icon;
final String? label;
final String? badge;
}
/// Extension pour créer facilement des onglets modernes
extension ModernTabExtension on Tab {
static ModernTab modern({
required IconData icon,
String? label,
String? badge,
}) {
return ModernTab(
icon: icon,
label: label,
badge: badge,
);
}
}