15 — Flutter Implementation Guide (Parents Module)
- 1. Folder structure
- 2. Dependencies
- 3. Cubits
- 4. ParentsRepository (single)
- 5. Navigation (go_router)
- 6. Theme
- 7. Extensions
- 8. Localization keys
- 9. Storage & security
- 10. Testing
- 11. Performance
- 12. Proposals flagged to the team
How to build the Parents feature in the Flutter client on top of 00-shared/11. Forward-looking spec; no client repo exists yet.
1. Folder structure
features/parents/
├── domain/
│ ├── models/
│ │ ├── parent.dart # ParentRef (schema mirror, parent.schema.ts:8-32)
│ │ ├── link.dart # Link + RelationshipType enum (student-parent-link.schema.ts:7-41)
│ │ ├── student_ref.dart # join view (student.schema.ts:16-64)
│ │ └── parent_user.dart # displayName/photo from users join
│ └── exceptions/parents_exceptions.dart
├── data/
│ ├── dto/
│ │ ├── create_parent_dto.dart
│ │ ├── update_parent_dto.dart
│ │ └── link_parent_dto.dart
│ └── repositories/
│ └── parents_repository.dart
└── presentation/
├── cubit/
│ ├── parents_cubit.dart
│ ├── parent_detail_cubit.dart
│ ├── parent_form_cubit.dart
│ ├── link_cubit.dart
│ ├── unlink_cubit.dart
│ ├── parent_children_cubit.dart # (forward-looking)
│ ├── child_switcher_cubit.dart # (forward-looking)
│ └── my_profile_cubit.dart # (forward-looking)
├── pages/
│ ├── parents_page.dart
│ ├── parent_detail_page.dart
│ ├── parent_form_page.dart
│ ├── link_sheet.dart
│ ├── my_children_page.dart # (forward-looking)
│ └── my_profile_page.dart # (forward-looking)
└── widgets/
├── parent_list_tile.dart
├── linked_child_card.dart
├── relationship_chip.dart
├── primary_guardian_badge.dart
├── emergency_flags_row.dart
├── user_picker_field.dart
└── child_switcher_bar.dart
2. Dependencies
Base set from 00-shared/11 §1 (flutter_bloc, dio, go_router, get_it, intl,
cached_network_image, connectivity_plus, secure_storage). No module-specific
package needed (dropdowns/switches are material).
3. Cubits
ParentsCubitusesPaginatedListMixin<ParentRef>(00-shared/06 §3.2);loadMore()guarded byhasNext;qnever sent (server ignores — OQ-7).ParentDetailCubitorchestrates the join:parent(id)+parentLinks(id)+ dedupedstudent(id)per link (00-shared/11 §4DTO→model mapping).LinkCubitowns the two pre-check warnings (duplicate pair, second primary) over the loaded links set; submit disabled onwarnDuplicate.ChildSwitcherCubit:selectedChildIdonly; read by other modules via injected instance (in-memory, session-scoped).
4. ParentsRepository (single)
class ParentsRepository {
// throws ApiException(code,status) via AppDio error interceptor
Future<Paginated<ParentRef>> parents({int page = 1, int limit = 20});
Future<ParentRef> parent(String id);
Future<List<Link>> parentLinks(String parentId); // GET /parents/:id/students
Future<List<Link>> studentLinks(String studentId); // GET /parents/link/student/:studentId
Future<ParentRef> createParent(CreateParentDto dto);
Future<ParentRef> updateParent(String id, UpdateParentDto dto);
Future<void> deleteParent(String id); // soft delete
Future<Link> linkParent(String studentId, LinkParentDto dto); // POST /parents/link/:studentId
Future<void> unlink(String linkId); // DELETE /parents/link/:linkId
Future<StudentRef> student(String id); // join helper → students repo
}
- Identity join: parent display name/avatar come from
users—ParentsRepositoryneeds auser(id)/users-search helper (users.service.ts:90-115), orParentRefcarriesuserIdand the UI joins lazily.
5. Navigation (go_router)
GoRoute /parents → ParentsPage (guard: parent.read planned)
├─ /parents/new → ParentFormPage(create)
├─ /parents/:id → ParentDetailPage
│ └─ /parents/:id/edit → ParentFormPage(edit)
└─ (sheet) /parents/link → LinkSheet (modal)
/my/children → MyChildrenPage (parent role, forward-looking)
/my/profile → MyProfilePage (forward-looking)
- Route guards mirror
00-shared/05 §9; until RBAC lands, guard on role client-side (parent.readabsent today — OQ-8) and hide admin workspace forparentrole. - Deep links
studylyon://parents/:id,studylyon://students/:id(linked-only check).
6. Theme
AppTheme.light()/dark() unchanged (00-shared/04 §1); module adds no tokens; primary
badge uses colorScheme.primaryContainer.
7. Extensions
RelationshipType.label(context)→ i18n + icon map.Link.isPrimaryshorthand;StudentRef.gradeLabel(context).int.tabular()for priority/income numerals (FontFeature.tabularFigures,02 §2).
8. Localization keys
parents.list.*, parents.detail.*, parents.form.*, parents.link.*,
parents.unlink.*, parents.relationship.*, parents.my.* (en .arb first;
relationship labels for mother/father/guardian/grandparent/relative/foster_parent
localized per org demand, 00-shared/11 §9).
9. Storage & security
- No sensitive data in this module; list last-good cache in
shared_preferences/Hive key{tenant}:parents:{page}(00-shared/11 §11); never cache link data for theparentrole (privacy boundary).
10. Testing
- Unit: cubits with mocked repo — pagination append/refresh; duplicate-link pre-check; primary-switch two-step (unlink→recreate) failure rollback; 404 mapping.
- Widget: list 3 states; detail children skeleton/empty/error; form 409 banner; link sheet warnings; switcher selection.
- Golden: components + pages light/dark × 3 sizes (
00-shared/10 §9). - Integration: create user → create parent → link → unlink → delete (mock server); offline list banner.
- E2E (P0): admin links guardian to student; verify row + primary badge; unlink confirm; delete parent; parent-login my-children (once endpoint lands).
11. Performance
ListView.builderfor parents list + children section; const constructors; avatar imagescached_network_imagewith resize; dedupe map for student joins; no rebuild of full detail on child-card state change (RepaintBoundary).
12. Proposals flagged to the team
- Backend: add
GET /parents/me(oruserIdfilter) + link-populated children endpoint — unblocks the entire parent self-service surface (OQ-1). - Backend: unique index on
(tenantId, studentId, parentId)+ 409 on duplicate link; enforcefindByIdresult inlinkStudentParent(OQ-2/OQ-3). - Backend: link PATCH endpoint (or switch-primary endpoint) to replace the delete+recreate workaround (OQ-4); consider single-primary rule (OQ-5).
- Backend:
@IsEnum(RelationshipType)inLinkParentDto(OQ-6). - Backend/RBAC: seed
parent.*permissions (04-Modules/Parents.md:64-70) and decorate endpoints (OQ-8). - Analytics events (
parents.*) waitAnalyticsService(00-shared/10 §8).