critical priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

BufdirFieldRowWidget is a StatelessWidget accepting a BufdirFieldRowData parameter
Layout uses a Row widget with two Expanded children; label takes flex 2, value takes flex 3 (or design-token-specified ratio)
Typography uses BufdirAccessibilityUtils tokens: label uses labelMedium style, value uses bodyMedium style
Minimum row height meets WCAG 2.2 touch target minimum of 44×44 logical pixels
Text color contrast ratio meets WCAG 2.2 AA (4.5:1) for normal body text against the row background color
Widget renders correctly in both light and dark theme (if theme switching is supported by the design system)
Long label text wraps gracefully and does not overflow; long value text also wraps
Golden test or widget test confirms layout at 375px width (iPhone SE viewport)
Widget is not responsible for icon or tooltip rendering at this stage — validationState is accepted but ignored

Technical Requirements

frameworks
Flutter
BufdirAccessibilityUtils (internal)
design token system
data models
BufdirFieldRowData
performance requirements
Widget must be const-constructable when BufdirFieldRowData is available at compile time
No rebuilds triggered by external state — pure StatelessWidget
security requirements
Label and value strings must be rendered as plain Text widgets — do not use HTML/RichText with raw HTML input
ui components
Row
Expanded
Text
Padding
SizedBox

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Import design tokens from BufdirAccessibilityUtils — do not hardcode colors or font sizes. Use Theme.of(context).textTheme.labelMedium and bodyMedium mapped to Bufdir tokens rather than raw TextStyle. For the two-column layout, use Row > [Expanded(flex:2, child: Text(label)), Expanded(flex:3, child: Text(value))]. Wrap the Row in a Padding using BufdirAccessibilityUtils.rowVerticalPadding and BufdirAccessibilityUtils.rowHorizontalPadding.

Add a ConstrainedBox with minHeight: 44.0 around the Row to satisfy WCAG touch target requirements even for single-line rows. Avoid IntrinsicHeight unless necessary as it is expensive in lists. This widget will be embedded in ListView.builder inside BufdirReportSectionWidget, so ensure it has no internal scroll.

Testing Requirements

Write widget tests in test/features/bufdir/widgets/bufdir_field_row_widget_test.dart. Cover: (1) label text renders in the left column; (2) value text renders in the right column; (3) row minimum height is at least 44px (use tester.getSize); (4) long strings wrap without overflow errors (use overflowWidgets matcher or check no RenderFlex overflow in logs); (5) widget accepts BufdirFieldRowData with validationState=warning without throwing (icon rendering comes in task-003). Add a golden test at 375px width for the normal state. Run flutter test --update-goldens once to capture baseline.

Component
Bufdir Field Row Widget
ui medium
Epic Risks (2)
high impact medium prob technical

Implementing the tap-to-scroll-and-focus behavior from the validation banner to a specific field row in a long scrollable list is complex in Flutter. If focus management is incorrectly implemented, VoiceOver users who navigate to the banner and select an issue will not be moved to the relevant field row, breaking the accessibility workflow and violating WCAG 2.4.3 (Focus Order).

Mitigation & Contingency

Mitigation: Use BufdirAccessibilityUtils focus management utilities (built in the foundation epic) with explicit GlobalKey-based scroll anchors on each field row. Test with a real iOS device running VoiceOver during widget development, not only in the Flutter accessibility inspector.

Contingency: If programmatic scroll-to-focus cannot be reliably achieved before the TestFlight deadline, fall back to a navigation approach where tapping a banner issue opens a modal detail sheet for that field row rather than scrolling in place, and file a follow-up ticket for the inline scroll implementation.

medium impact low prob technical

The validation summary banner must reactively update its issue count as underlying aggregated data changes (e.g., if the coordinator has navigated away and data was refreshed). If the banner's Riverpod provider is not correctly scoped, it may display stale issue counts or fail to disappear when all issues are resolved, eroding coordinator trust in the validation system.

Mitigation & Contingency

Mitigation: Drive the banner exclusively from the same Riverpod provider that powers the full preview model — do not maintain a separate local state for issue counts. Write a widget test that simulates a data refresh mid-review and asserts the banner updates within one frame.

Contingency: If stale state reaches production, add a manual refresh button to the banner as a short-term workaround while the provider scoping is corrected in the next release cycle.