diff --git a/lib/src/core/design_system/components/atoms/profile_tab_item.dart b/lib/src/core/design_system/components/atoms/profile_tab_item.dart index 5c584c29..e957e0ba 100644 --- a/lib/src/core/design_system/components/atoms/profile_tab_item.dart +++ b/lib/src/core/design_system/components/atoms/profile_tab_item.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:spark/src/core/design_system/components/atoms/tab_item.dart'; +const double _profileTabIndicatorWidth = 24; + class ProfileTabItem extends StatelessWidget { const ProfileTabItem({ required this.icon, @@ -24,6 +26,7 @@ class ProfileTabItem extends StatelessWidget { isSelected: isSelected, onTap: onTap, indicatorColor: theme.colorScheme.primary, + indicatorWidth: _profileTabIndicatorWidth, ); } } diff --git a/lib/src/core/design_system/components/atoms/tab_item.dart b/lib/src/core/design_system/components/atoms/tab_item.dart index 68231987..b25585cf 100644 --- a/lib/src/core/design_system/components/atoms/tab_item.dart +++ b/lib/src/core/design_system/components/atoms/tab_item.dart @@ -10,6 +10,7 @@ class AppTabItem extends StatelessWidget { required this.onTap, this.indicatorColor, this.indicatorThickness = 2, + this.indicatorWidth, this.padding = const EdgeInsets.symmetric(vertical: 12), super.key, }); @@ -20,6 +21,7 @@ class AppTabItem extends StatelessWidget { final VoidCallback onTap; final Color? indicatorColor; final double indicatorThickness; + final double? indicatorWidth; final EdgeInsetsGeometry padding; @override @@ -27,22 +29,50 @@ class AppTabItem extends StatelessWidget { final theme = Theme.of(context); return Expanded( - child: InteractivePressable( - onTap: onTap, - child: Container( - padding: padding, - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: isSelected - ? (indicatorColor ?? theme.colorScheme.primary) - : const Color(0x00000000), - width: indicatorThickness, + child: Stack( + alignment: Alignment.center, + children: [ + InteractivePressable( + onTap: onTap, + overlayColor: Colors.transparent, + child: SizedBox( + width: double.infinity, + child: Padding( + padding: padding, + child: Center(child: isSelected ? activeChild : inactiveChild), ), ), ), - child: isSelected ? activeChild : inactiveChild, - ), + if (indicatorWidth == null) + Positioned( + left: 0, + right: 0, + bottom: 0, + child: IgnorePointer( + child: ColoredBox( + color: isSelected + ? (indicatorColor ?? theme.colorScheme.primary) + : Colors.transparent, + child: SizedBox(height: indicatorThickness), + ), + ), + ) + else + Positioned( + bottom: 0, + child: IgnorePointer( + child: ColoredBox( + color: isSelected + ? (indicatorColor ?? theme.colorScheme.primary) + : Colors.transparent, + child: SizedBox( + width: indicatorWidth, + height: indicatorThickness, + ), + ), + ), + ), + ], ), ); } diff --git a/test/src/core/design_system/components/interactive_atoms_test.dart b/test/src/core/design_system/components/interactive_atoms_test.dart index c81af38e..e85e3ba4 100644 --- a/test/src/core/design_system/components/interactive_atoms_test.dart +++ b/test/src/core/design_system/components/interactive_atoms_test.dart @@ -115,6 +115,46 @@ void main() { expect(tapCount, 1); }); + testWidgets('AppTabItem keeps selected indicator outside press feedback', ( + tester, + ) async { + await tester.pumpWidget( + MaterialApp( + home: Center( + child: SizedBox( + width: 300, + child: Row( + children: [ + AppTabItem( + activeChild: const Text('Selected'), + inactiveChild: const Text('Unselected'), + isSelected: true, + onTap: () {}, + indicatorColor: Colors.red, + indicatorWidth: 24, + ), + ], + ), + ), + ), + ), + ); + + final indicatorFinder = find.byWidgetPredicate( + (widget) => widget is ColoredBox && widget.color == Colors.red, + ); + + expect(indicatorFinder, findsOneWidget); + expect(tester.getSize(indicatorFinder), const Size(24, 2)); + expect( + find.ancestor( + of: indicatorFinder, + matching: find.byType(InteractivePressable), + ), + findsNothing, + ); + }); + testWidgets('ToggleButton calls onChanged with inverse selected value', ( tester, ) async {