Multi-Select: select-all / deselect-all controls and count badge
epic-bulk-and-proxy-registration-mentor-selection-task-009 — Add a select-all / deselect-all toggle control at the top of the Peer Mentor Multi-Select List. Implement a live selected-count badge that updates immediately as items are checked or unchecked. The badge must be announced via a Semantics live region so screen reader users receive updates without losing focus. Select-all must handle large lists (1 400+ chapters in NHF) without UI jank.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
Store the selection as a Set
Flutter's Semantics system will announce label changes automatically on iOS (VoiceOver) and Android (TalkBack). For the tri-state indeterminate visual, use a custom painter or an Icon(Icons.remove) overlay on the checkbox rather than Flutter's built-in Checkbox which lacks indeterminate state natively. Apply design tokens for spacing (e.g., AppSpacing.sm) and typography to the badge to stay consistent with the design system.
Testing Requirements
Widget tests (flutter_test): (1) Render the list with 5 mock mentors, tap 'Select All', assert all checkboxes are checked and badge shows '5 selected'. (2) Tap 'Deselect All', assert all unchecked and badge shows '0 selected'. (3) Tap one item, assert tri-state indeterminate indicator appears. (4) Assert Semantics tree contains a live region node whose label matches the count badge text.
Performance test: pump a 1400-item list, invoke select-all event, verify frame budget is not exceeded using Flutter's test binding timeDilation. Accessibility test: use flutter_test SemanticsController to verify the live region label updates after selection events.
NHF coordinators may manage dozens of peer mentors across multiple chapters. If the multi-select list renders all contacts in a single unsorted ListView, performance degrades with 50+ items, and coordinators cannot efficiently locate a specific mentor, increasing the probability of selection errors and wrong-person proxy registrations.
Mitigation & Contingency
Mitigation: Use a SliverList with itemExtent for fixed-height rows to enable O(1) scroll position calculation. Implement the search filter using a debounce utility operating on an in-memory list (no extra API calls). Sort the contact list alphabetically by default. Add chapter-filter chips above the list for NHF's multi-chapter coordinators.
Contingency: If performance issues arise in testing with real data sets, introduce pagination with a 'load more' trigger at the bottom of the list and cache rendered rows using Flutter's AutomaticKeepAliveClientMixin.
NHF has a complex 12-national-association / 9-region / 1,400-chapter hierarchy. It is ambiguous whether a coordinator can proxy-register for peer mentors outside their immediately assigned chapter. If the contact list is not correctly scoped by RLS, coordinators might see — and register on behalf of — peer mentors they do not manage, creating fraudulent activity records that skew Bufdir statistics.
Mitigation & Contingency
Mitigation: The Proxy Contact List Provider must query only peer mentors linked to the coordinator's own chapter scope via RLS. Add an explicit Supabase query test asserting that a coordinator from chapter A cannot retrieve peer mentors from chapter B. Display each mentor's chapter affiliation in the list row so coordinators can visually verify scope.
Contingency: If RLS scope is found to be too permissive in testing, apply a server-side coordinator_id filter as a secondary guard on the query. Block the feature release until the scope test passes consistently.