@@ -15,6 +15,7 @@ library;
1515
1616import 'package:flutter/material.dart' ;
1717import 'package:ouds_core/components/control/internal/interaction/ouds_inherited_interaction_model.dart' ;
18+ import 'package:ouds_core/components/navigation/internal/ouds_navigation_a11y.dart' ;
1819import 'package:ouds_core/components/navigation/internal/ouds_navigation_bar_background_modifier.dart' ;
1920import 'package:ouds_core/components/navigation/internal/ouds_navigation_bar_border_modifier.dart' ;
2021import 'package:ouds_core/components/navigation/internal/ouds_navigation_bar_state.dart' ;
@@ -126,24 +127,39 @@ class _OudsNavigationBarState extends State<OudsNavigationBar> {
126127 @override
127128 void initState () {
128129 super .initState ();
129- _selectedIndex = widget.selectedIndex.clamp (0 , widget.destinations.length - 1 );
130+ _selectedIndex = widget.selectedIndex.clamp (
131+ 0 ,
132+ widget.destinations.length - 1 ,
133+ );
130134 }
131135
132136 /// Updates the selected index if [currentIndex] changes.
133137 @override
134138 void didUpdateWidget (covariant OudsNavigationBar oldWidget) {
135139 super .didUpdateWidget (oldWidget);
136140 if (widget.selectedIndex != oldWidget.selectedIndex) {
137- _selectedIndex = widget.selectedIndex.clamp (0 , widget.destinations.length - 1 );
141+ _selectedIndex = widget.selectedIndex.clamp (
142+ 0 ,
143+ widget.destinations.length - 1 ,
144+ );
138145 }
139146 }
140147
141148 /// Builds the navigation bar with dynamic label and icon colors and a custom indicator shape.
142149 @override
143150 Widget build (BuildContext context) {
144- final interactionModelHover = OudsInheritedInteractionModel .of (context, InteractionAspect .hover);
145- final interactionModelPressed = OudsInheritedInteractionModel .of (context, InteractionAspect .pressed);
146- final interactionModelFocused = OudsInheritedInteractionModel .of (context, InteractionAspect .focused);
151+ final interactionModelHover = OudsInheritedInteractionModel .of (
152+ context,
153+ InteractionAspect .hover,
154+ );
155+ final interactionModelPressed = OudsInheritedInteractionModel .of (
156+ context,
157+ InteractionAspect .pressed,
158+ );
159+ final interactionModelFocused = OudsInheritedInteractionModel .of (
160+ context,
161+ InteractionAspect .focused,
162+ );
147163
148164 final isHovered = interactionModelHover? .state.isHovered ?? false ;
149165 final isPressed = interactionModelPressed? .state.isPressed ?? false ;
@@ -158,57 +174,72 @@ class _OudsNavigationBarState extends State<OudsNavigationBar> {
158174
159175 final barControlState = barStateDeterminer.determineControlState ();
160176 final navigationBarModifier = OudsNavigationBarStatusModifier (context);
161- final navigationBarBgModifier = OudsNavigationBarBackgroundColorModifier (context);
162- final navigationBarBorderModifier = OudsNavigationBarBorderModifier (context);
177+ final navigationBarBgModifier = OudsNavigationBarBackgroundColorModifier (
178+ context,
179+ );
180+ final navigationBarBorderModifier = OudsNavigationBarBorderModifier (
181+ context,
182+ );
163183
164184 final safeIndex = _selectedIndex.clamp (0 , widget.destinations.length - 1 );
165185
166- return ClipRect (
167- child: BackdropFilter (
168- filter: navigationBarBorderModifier.getBlurNavigationBar (),
169- child: Container (
170- decoration: BoxDecoration (
171- border: navigationBarBorderModifier.getBorderNavigationBar (),
172- ),
173- child: NavigationBar (
174- height: _oudsNavigationBarHeight,
175- selectedIndex: safeIndex,
176- // `indicatorColor` paints the Material 3 active indicator behind the selected destination.
177- indicatorColor: navigationBarModifier.getMaterialIndicatorBarColor (
178- barControlState,
179- widget.onDestinationSelected != null ,
186+ // Wrap the entire navigation bar with accessibility text scaling constraints
187+ // to prevent item overflow when zoom is enabled. The maxScaleFactor of 1.08 (108%) ensures
188+ // that the 26px icon scales to 28.08px at maximum zoom. This constraint is applied at the bar level
189+ // to ensure consistent scaling across all items and work seamlessly with composite semantic labels.
190+ return OudsNavigationA11y .withA11yScaling (
191+ ClipRect (
192+ child: BackdropFilter (
193+ filter: navigationBarBorderModifier.getBlurNavigationBar (),
194+ child: Container (
195+ decoration: BoxDecoration (
196+ border: navigationBarBorderModifier.getBorderNavigationBar (),
180197 ),
181- // `overlayColor` is the transient ink overlay used for interaction feedback (pressed/hovered/focused),
182- // resolved per destination via `WidgetState`.
183- overlayColor: WidgetStateProperty .resolveWith <Color >(
184- (states) {
198+ child: NavigationBar (
199+ height: _oudsNavigationBarHeight,
200+ selectedIndex: safeIndex,
201+ indicatorColor: navigationBarModifier
202+ .getMaterialIndicatorBarColor (
203+ barControlState,
204+ widget.onDestinationSelected != null ,
205+ ),
206+ overlayColor: WidgetStateProperty .resolveWith <Color >((states) {
185207 final isSelected = states.contains (WidgetState .selected);
186- return navigationBarModifier.getMaterialIndicatorBarColor (barControlState, isSelected);
187- },
188- ),
189- backgroundColor: navigationBarBgModifier.getBackgroundColor (widget.translucent),
190- // Label text style resolved per destination via `WidgetState` (at minimum selected/unselected).
191- labelTextStyle: WidgetStateProperty .resolveWith <TextStyle >(
192- (states) {
208+ return navigationBarModifier.getMaterialIndicatorBarColor (
209+ barControlState,
210+ isSelected,
211+ );
212+ }),
213+ backgroundColor: navigationBarBgModifier.getBackgroundColor (
214+ widget.translucent,
215+ ),
216+ labelTextStyle: WidgetStateProperty .resolveWith <TextStyle >((
217+ states,
218+ ) {
193219 final isSelected = states.contains (WidgetState .selected);
194- return OudsTheme .of (context).typographyTokens.typeLabelDefaultMedium (context).copyWith (
195- color: navigationBarModifier.getTextIconItemColor (barControlState, isSelected),
220+ return OudsTheme .of (context).typographyTokens
221+ .typeLabelDefaultMedium (context)
222+ .copyWith (
223+ color: navigationBarModifier.getTextIconItemColor (
224+ barControlState,
225+ isSelected,
226+ ),
196227 );
197- },
198- ),
199- destinations : List . generate (
200- widget.destinations.length,
201- (index) => widget.destinations[index]. toNavigationDestination (
202- context ,
203- barControlState ,
204- isSelected : index == safeIndex ,
228+ }) ,
229+ destinations : List . generate (
230+ widget.destinations.length,
231+ (index) => widget.destinations[index]. toNavigationDestination (
232+ context,
233+ barControlState ,
234+ isSelected : index == safeIndex ,
235+ ) ,
205236 ),
237+ onDestinationSelected: (index) {
238+ if (index == safeIndex) return ;
239+ setState (() => _selectedIndex = index);
240+ widget.onDestinationSelected? .call (index);
241+ },
206242 ),
207- onDestinationSelected: (index) {
208- if (index == safeIndex) return ;
209- setState (() => _selectedIndex = index);
210- widget.onDestinationSelected? .call (index);
211- },
212243 ),
213244 ),
214245 ),
0 commit comments