Skip to content

Commit 221eb8b

Browse files
committed
chore(A11Y): update accessibility Bottom Bar
1 parent 51c9edc commit 221eb8b

4 files changed

Lines changed: 222 additions & 68 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* // Software Name: OUDS Flutter
3+
* // SPDX-FileCopyrightText: Copyright (c) Orange SA
4+
* // SPDX-License-Identifier: MIT
5+
* //
6+
* // This software is distributed under the MIT license,
7+
* // the text of which is available at https://opensource.org/license/MIT/
8+
* // or see the "LICENSE" file for more details.
9+
* //
10+
* // Software description: Flutter library of reusable graphical components
11+
* //
12+
*/
13+
14+
import 'package:flutter/material.dart';
15+
16+
/// Accessibility constants for OUDS Navigation components.
17+
class OudsNavigationA11y {
18+
/// Maximum text scale factor for navigation items.
19+
///
20+
/// Clamped to 108% to prevent item overflow including:
21+
/// - Selected indicator bar
22+
/// - Badge notifications
23+
/// - Icon and label scaling
24+
///
25+
/// At 108%, the 26px icon scales to 28.08px, providing adequate spacing
26+
/// without overflow in the navigation bar container.
27+
static const double maxTextScaleFactor = 1.08;
28+
29+
/// Wraps a widget with text scaling constraints for navigation components.
30+
///
31+
/// This ensures consistent accessibility behavior across all navigation items
32+
/// by limiting text enlargement to [maxTextScaleFactor], preventing overflow
33+
/// of items with selected indicators and badges.
34+
///
35+
/// Parameters:
36+
/// - [child]: The widget to wrap with text scaling constraints
37+
///
38+
/// Returns:
39+
/// A widget with clamped text scaling applied, allowing zoom up to 108%
40+
/// while maintaining proper item sizing and avoiding overflow.
41+
static Widget withA11yScaling(Widget child) {
42+
return MediaQuery.withClampedTextScaling(
43+
minScaleFactor: 1.0,
44+
maxScaleFactor: maxTextScaleFactor,
45+
child: child,
46+
);
47+
}
48+
}

ouds_core/lib/components/navigation/ouds_navigation_bar.dart

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ library;
1515

1616
import 'package:flutter/material.dart';
1717
import 'package:ouds_core/components/control/internal/interaction/ouds_inherited_interaction_model.dart';
18+
import 'package:ouds_core/components/navigation/internal/ouds_navigation_a11y.dart';
1819
import 'package:ouds_core/components/navigation/internal/ouds_navigation_bar_background_modifier.dart';
1920
import 'package:ouds_core/components/navigation/internal/ouds_navigation_bar_border_modifier.dart';
2021
import '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,12 +174,16 @@ 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(
186+
final navigationBarContent = ClipRect(
167187
child: BackdropFilter(
168188
filter: navigationBarBorderModifier.getBlurNavigationBar(),
169189
child: Container(
@@ -173,29 +193,36 @@ class _OudsNavigationBarState extends State<OudsNavigationBar> {
173193
child: NavigationBar(
174194
height: _oudsNavigationBarHeight,
175195
selectedIndex: safeIndex,
176-
// `indicatorColor` paints the Material 3 active indicator behind the selected destination.
196+
// ...existing code...
177197
indicatorColor: navigationBarModifier.getMaterialIndicatorBarColor(
178198
barControlState,
179199
widget.onDestinationSelected != null,
180200
),
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) {
185-
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) {
193-
final isSelected = states.contains(WidgetState.selected);
194-
return OudsTheme.of(context).typographyTokens.typeLabelDefaultMedium(context).copyWith(
195-
color: navigationBarModifier.getTextIconItemColor(barControlState, isSelected),
196-
);
197-
},
201+
// ...existing code...
202+
overlayColor: WidgetStateProperty.resolveWith<Color>((states) {
203+
final isSelected = states.contains(WidgetState.selected);
204+
return navigationBarModifier.getMaterialIndicatorBarColor(
205+
barControlState,
206+
isSelected,
207+
);
208+
}),
209+
backgroundColor: navigationBarBgModifier.getBackgroundColor(
210+
widget.translucent,
198211
),
212+
// ...existing code...
213+
labelTextStyle: WidgetStateProperty.resolveWith<TextStyle>((
214+
states,
215+
) {
216+
final isSelected = states.contains(WidgetState.selected);
217+
return OudsTheme.of(context).typographyTokens
218+
.typeLabelDefaultMedium(context)
219+
.copyWith(
220+
color: navigationBarModifier.getTextIconItemColor(
221+
barControlState,
222+
isSelected,
223+
),
224+
);
225+
}),
199226
destinations: List.generate(
200227
widget.destinations.length,
201228
(index) => widget.destinations[index].toNavigationDestination(
@@ -213,5 +240,8 @@ class _OudsNavigationBarState extends State<OudsNavigationBar> {
213240
),
214241
),
215242
);
243+
244+
// Apply accessibility text scaling constraints to prevent item overflow
245+
return OudsNavigationA11y.withA11yScaling(navigationBarContent);
216246
}
217247
}

0 commit comments

Comments
 (0)