Implement localised access-denial message
epic-no-access-screen-ui-task-003 — Add the localised human-readable explanation string to the widget body explaining why access is denied (global admins must use the web portal). Source the string from the app's ARB/l10n localisation system. Apply the correct text style from the design token text style system with sufficient contrast ratio.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Retrieve the string using context.l10n.noAccessScreenDenialMessage (generated AppLocalizations accessor). Add the ARB key to all locale files before implementing the widget. Apply the text style token by referencing the design token provider rather than using Theme.of(context).textTheme directly, to stay consistent with the rest of the codebase. Use a Flexible or Expanded ancestor if necessary to prevent overflow inside the Column.
The message should explain clearly that the global admin role is not supported in the mobile app and that the user should visit the web admin portal — keep it concise and non-technical for accessibility.
Testing Requirements
Write widget tests using flutter_test. Test 1: pump the widget with a real LocalizationsDelegate and assert the denial message string appears in the widget tree. Test 2: pump with a mock locale (en) and verify the correct English string is displayed. Test 3: wrap the widget in a MediaQuery with textScaleFactor: 2.0 and assert no RenderFlex overflow errors.
Test 4: verify the TextStyle applied matches the expected design token style. Use goldenkit or matchesGoldenFile for visual regression if the project uses golden tests.
Flutter's live region (SemanticsProperties.liveRegion) announcement may be delayed or swallowed by the OS accessibility engine if the Semantics tree is not fully built when the screen mounts, causing screen-reader users to miss the denial announcement.
Mitigation & Contingency
Mitigation: Trigger the live region announcement from a post-frame callback (WidgetsBinding.addPostFrameCallback) to ensure the Semantics tree is committed before the announcement fires. Test on both VoiceOver (iOS) and TalkBack (Android) physical devices.
Contingency: If live region timing is unreliable, fall back to using SemanticsService.announce() directly in the initState post-frame callback, which provides more deterministic announcement timing.
The organisation logo may fail to load (network error, missing asset) leaving a broken image in an otherwise functional screen, degrading the professional appearance and potentially confusing users.
Mitigation & Contingency
Mitigation: Wrap the logo widget in an error builder that renders a styled fallback (organisation name text or a generic icon) when the logo asset or network image fails to load.
Contingency: If logo loading is persistently unreliable across organisations, remove the logo from the no-access screen entirely in favour of a text-only header using the organisation's display name from the design token system.