🔠 Text scaling
Common mistake
A common mistake is ignoring the user’s system font size setting. Text is sized with hard-coded pixel values and no
TextScaleris applied, so the layout looks identical regardless of the accessibility settings the user has configured. Users who need larger text cannot benefit from their own system preferences.
🎯 Relevant elements
This guideline applies to every widget that renders text:
- Body text and labels (
Text,RichText)- Button labels
- Dialog and bottom sheet content
- List item titles and subtitles
- Any custom text widget that sets
fontSizeexplicitly
WCAG Guideline
This guideline is based on WCAG 2.2 — 1.4.4 Resize Text (Level AA). Text must be resizable up to 200 percent without loss of content or functionality, except for captions and images of text.
Solution
Flutter’s Text widget respects the system text scale automatically through MediaQuery.textScalerOf(context). The main risk is suppressing that behaviour unintentionally.
✅ Respect the system text scale (default behaviour)
Do not pass a hard-coded textScaleFactor or textScaler to MediaQuery. Let Flutter resolve the scale from the system.
// ✅ Do - Text respects the system text scale automatically
Text(
'Hello, world!',
style: const TextStyle(fontSize: 16),
);
✅ Apply the scale explicitly when computing custom sizes
When you calculate a font size at runtime, apply the system scale manually so that the computed size also scales.
// ✅ Do - scale the computed font size with the system scaler
final fontSize = MediaQuery.textScalerOf(context).scale(16);
Text(
'Hello, world!',
style: TextStyle(fontSize: fontSize),
);
⚠️ Suppress scaling only on layout-critical components
Some components have strict layout constraints (e.g. a carousel item with a fixed tile height). In these cases, suppressing the text scale may be necessary to prevent overflow, but it should be treated as an accessibility trade-off and used sparingly.
// ⚠️ Only use when layout overflow cannot be solved any other way
MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(1.0), // fixed scale, ignores system setting
),
child: Text(
'Carousel title',
style: const TextStyle(fontSize: 14),
),
);
Prefer solving overflow through layout instead:
// ✅ Prefer - allow the text to scale but clip gracefully
Text(
carouselTitle,
style: const TextStyle(fontSize: 14),
maxLines: 2,
overflow: TextOverflow.ellipsis,
);
Overflow strategies
When scaled text no longer fits its container, handle overflow explicitly rather than clipping or hiding content:
| Strategy | When to use |
|---|---|
TextOverflow.ellipsis | Short single-line labels where truncation is acceptable |
maxLines: n | Multi-line content with a maximum visible line count |
| Flexible/Expanded layout | Containers that can grow to fit the text |
| Scrollable content | Labels inside cards or tiles that may become long |
// ✅ Do - ellipsis for fixed-size tiles
Text(
artifactTitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 14),
);
Validation & Testing
See the Validation & Testing setup guide for tool configuration.
Manual testing
- Open the device Accessibility settings and increase the font size to the maximum (or 200 %).
- Navigate through the app and verify that all text is readable — no content is clipped, overflows its container, or disappears entirely.
- Reduce the font size back to the default and confirm the layout looks unchanged.
TalkBack & VoiceOver
Enable the screen reader and navigate through text-heavy screens. Confirm that all text is announced in full, including text that is visually truncated (the semantic label must contain the complete string).
accessibility_tools
The accessibility_tools package flags text that is hard-coded to a fixed scale factor. Enable Screen reader mode and inspect the overlay for any label that appears truncated or hidden.
flutter_test
Use MediaQuery overrides to simulate different text scales and assert that no content is clipped.
testWidgets('text scales to 200% without overflow', (tester) async {
await tester.pumpWidget(
MediaQuery(
data: const MediaQueryData(textScaler: TextScaler.linear(2.0)),
child: const MaterialApp(
home: Scaffold(
body: Text(
'Hello, world!',
style: TextStyle(fontSize: 16),
),
),
),
),
);
// No RenderFlex overflow errors at 2× scale
expect(tester.takeException(), isNull);
});
🛠️ Usage baseflow-a11y-components library
This is enforced on the following components:
- A11yText