From 799b7d8403c504bd66ff0cc13eee497d8ce1e8bc Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 9 Jan 2026 21:12:09 +0100 Subject: [PATCH 1/3] Implemented new nav bar with selected animation --- lib/core/custom_theme.dart | 2 + .../main_menu/custom_navigation_bar.dart | 89 ++++++++++--------- lib/presentation/widgets/navbar_item.dart | 69 ++++++++++++-- pubspec.yaml | 2 +- 4 files changed, 110 insertions(+), 52 deletions(-) diff --git a/lib/core/custom_theme.dart b/lib/core/custom_theme.dart index a6c6376..3f15ad9 100644 --- a/lib/core/custom_theme.dart +++ b/lib/core/custom_theme.dart @@ -11,6 +11,8 @@ class CustomTheme { static Color onBoxColor = const Color(0xFF181818); static Color boxBorder = const Color(0xFF272727); static const Color textColor = Colors.white; + static Color navBarItemSelectedColor = primaryColor.withValues(green: 0.4); + static Color navBarItemUnselectedColor = Colors.white.withValues(alpha: 0.6); // ==================== Border Radius ==================== static const double standardBorderRadius = 12.0; diff --git a/lib/presentation/views/main_menu/custom_navigation_bar.dart b/lib/presentation/views/main_menu/custom_navigation_bar.dart index a8b18c8..15f15b6 100644 --- a/lib/presentation/views/main_menu/custom_navigation_bar.dart +++ b/lib/presentation/views/main_menu/custom_navigation_bar.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/l10n/generated/app_localizations.dart'; @@ -70,50 +72,49 @@ class _CustomNavigationBarState extends State backgroundColor: CustomTheme.backgroundColor, body: tabs[currentIndex], extendBody: true, - bottomNavigationBar: SafeArea( - minimum: const EdgeInsets.only(bottom: 30), - child: Container( - margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 10), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(24), - color: CustomTheme.primaryColor, - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(24), - child: SizedBox( - height: 60, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - NavbarItem( - index: 0, - isSelected: currentIndex == 0, - icon: Icons.home_rounded, - label: loc.home, - onTabTapped: onTabTapped, - ), - NavbarItem( - index: 1, - isSelected: currentIndex == 1, - icon: Icons.gamepad_rounded, - label: loc.matches, - onTabTapped: onTabTapped, - ), - NavbarItem( - index: 2, - isSelected: currentIndex == 2, - icon: Icons.group_rounded, - label: loc.groups, - onTabTapped: onTabTapped, - ), - NavbarItem( - index: 3, - isSelected: currentIndex == 3, - icon: Icons.bar_chart_rounded, - label: loc.statistics, - onTabTapped: onTabTapped, - ), - ], + bottomNavigationBar: ClipRRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Container( + decoration: BoxDecoration( + color: CustomTheme.boxColor.withValues(alpha: 0.8), + ), + child: SafeArea( + child: SizedBox( + height: 70, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + NavbarItem( + index: 0, + isSelected: currentIndex == 0, + icon: Icons.home_rounded, + label: loc.home, + onTabTapped: onTabTapped, + ), + NavbarItem( + index: 1, + isSelected: currentIndex == 1, + icon: Icons.gamepad_rounded, + label: loc.matches, + onTabTapped: onTabTapped, + ), + NavbarItem( + index: 2, + isSelected: currentIndex == 2, + icon: Icons.group_rounded, + label: loc.groups, + onTabTapped: onTabTapped, + ), + NavbarItem( + index: 3, + isSelected: currentIndex == 3, + icon: Icons.bar_chart_rounded, + label: loc.statistics, + onTabTapped: onTabTapped, + ), + ], + ), ), ), ), diff --git a/lib/presentation/widgets/navbar_item.dart b/lib/presentation/widgets/navbar_item.dart index 13a8d4d..6e2e24e 100644 --- a/lib/presentation/widgets/navbar_item.dart +++ b/lib/presentation/widgets/navbar_item.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:game_tracker/core/custom_theme.dart'; /// A navigation bar item widget that represents a single tab in a navigation bar. /// - [index]: The index of the tab. @@ -35,7 +36,45 @@ class NavbarItem extends StatefulWidget { State createState() => _NavbarItemState(); } -class _NavbarItemState extends State { +class _NavbarItemState extends State + with SingleTickerProviderStateMixin { + /// Animation controller for the scale animation + late AnimationController _animationController; + + /// Scale animation for the icon when selected + late Animation _scaleAnimation; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + + _scaleAnimation = Tween(begin: 1.0, end: 1.2).animate( + CurvedAnimation( + parent: _animationController, + curve: Curves.easeInOutBack, + ), + ); + + if (widget.isSelected) { + _animationController.forward(); + } + } + + // Retrigger animation on selection change + @override + void didUpdateWidget(NavbarItem oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.isSelected && !oldWidget.isSelected) { + _animationController.forward(); + } else if (!widget.isSelected && oldWidget.isSelected) { + _animationController.reverse(); + } + } + @override Widget build(BuildContext context) { return Expanded( @@ -48,19 +87,29 @@ class _NavbarItemState extends State { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - widget.icon, - color: widget.isSelected ? Colors.white : Colors.black, + ScaleTransition( + scale: widget.isSelected + ? _scaleAnimation + : const AlwaysStoppedAnimation(1.0), + child: Icon( + widget.icon, + color: widget.isSelected + ? CustomTheme.navBarItemSelectedColor + : CustomTheme.navBarItemUnselectedColor, + size: 26, + ), ), const SizedBox(height: 4), Text( widget.label, style: TextStyle( - color: widget.isSelected ? Colors.white : Colors.black, - fontSize: 12, + color: widget.isSelected + ? CustomTheme.navBarItemSelectedColor + : CustomTheme.navBarItemUnselectedColor, + fontSize: widget.isSelected ? 12 : 11, fontWeight: widget.isSelected ? FontWeight.bold - : FontWeight.normal, + : FontWeight.w500, ), ), ], @@ -69,4 +118,10 @@ class _NavbarItemState extends State { ), ); } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } } diff --git a/pubspec.yaml b/pubspec.yaml index e79ca17..74df2a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: game_tracker description: "Game Tracking App for Card Games" publish_to: 'none' -version: 0.0.1+21 +version: 0.0.1+69 environment: sdk: ^3.8.1 -- 2.49.1 From 8ca4e3210e2b2e15263dbe33ff5bd7890fdb958e Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sun, 11 Jan 2026 11:28:21 +0100 Subject: [PATCH 2/3] remove button in match view for testing --- lib/presentation/views/main_menu/match_view/match_view.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index ecfa9ca..9da007b 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -105,7 +105,8 @@ class _MatchViewState extends State { ), Positioned( bottom: MediaQuery.paddingOf(context).bottom, - child: CustomWidthButton( + child: SizedBox.shrink() + /* CustomWidthButton( text: loc.create_match, sizeRelativeToWidth: 0.90, onPressed: () async { @@ -118,6 +119,7 @@ class _MatchViewState extends State { ); }, ), + */ ), ], ), -- 2.49.1 From ab20bd764b67e015e73b3957c6229e11c4bcecd3 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sun, 11 Jan 2026 11:30:00 +0100 Subject: [PATCH 3/3] implement draft blur navbar --- .../main_menu/custom_navigation_bar.dart | 61 ++++++++++++++++--- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/lib/presentation/views/main_menu/custom_navigation_bar.dart b/lib/presentation/views/main_menu/custom_navigation_bar.dart index 9c5c846..d3b7007 100644 --- a/lib/presentation/views/main_menu/custom_navigation_bar.dart +++ b/lib/presentation/views/main_menu/custom_navigation_bar.dart @@ -73,14 +73,59 @@ class _CustomNavigationBarState extends State backgroundColor: CustomTheme.backgroundColor, body: tabs[currentIndex], extendBody: true, - bottomNavigationBar: ClipRRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), - child: Container( - decoration: BoxDecoration( - color: CustomTheme.boxColor.withValues(alpha: 0.8), + bottomNavigationBar: SizedBox( + height: 70 + MediaQuery.of(context).padding.bottom, + child: Stack( + children: [ + // Dynamisch generierte Blur-Layer für ultra-smooth Übergang + ...List.generate(35, (index) { + // Verwende kubische Kurve für noch natürlicheren, weicheren Übergang + final progress = index / 34.0; // 0.0 bis 1.0 + final cubic = progress * progress * progress; // Kubische Kurve + final blurStrength = 0.5 + (cubic * 50.0); // Sehr sanft von 0.5 bis 50.5 + + // Höhe geht jetzt komplett von 100% bis 0% (ganz nach unten) + // Mit extra Dichte am unteren Ende für weicheren Übergang + final heightFactor = index < 25 + ? 1.0 - (progress * 0.7) // Erste 25 Layer: 100% bis 30% + : 0.3 - ((index - 25) / 34.0); // Letzte 10 Layer: 30% bis 0% (dichter) + + return Positioned( + left: 0, + right: 0, + bottom: 0, + height: (70 + MediaQuery.of(context).padding.bottom) * heightFactor.clamp(0.05, 1.0), + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: blurStrength, + sigmaY: blurStrength, + ), + child: Container( + color: Colors.transparent, + ), + ), + ), + ); + }), + // Gradient-Overlay + Positioned.fill( + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + CustomTheme.boxColor.withValues(alpha: 1), + CustomTheme.boxColor.withValues(alpha: 0.0), + ], + stops: const [0.4, 1], + ), + ), + ), ), - child: SafeArea( + // Navbar-Inhalt + SafeArea( child: SizedBox( height: 70, child: Row( @@ -118,7 +163,7 @@ class _CustomNavigationBarState extends State ), ), ), - ), + ], ), ), ); -- 2.49.1