Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

15 — Flutter Implementation Guide (Parents Module)

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

  • ParentsCubit uses PaginatedListMixin<ParentRef> (00-shared/06 §3.2); loadMore() guarded by hasNext; q never sent (server ignores — OQ-7).
  • ParentDetailCubit orchestrates the join: parent(id) + parentLinks(id) + deduped student(id) per link (00-shared/11 §4 DTO→model mapping).
  • LinkCubit owns the two pre-check warnings (duplicate pair, second primary) over the loaded links set; submit disabled on warnDuplicate.
  • ChildSwitcherCubit: selectedChildId only; 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 usersParentsRepository needs a user(id)/users-search helper (users.service.ts:90-115), or ParentRef carries userId and 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.read absent today — OQ-8) and hide admin workspace for parent role.
  • 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.isPrimary shorthand; 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 the parent role (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.builder for parents list + children section; const constructors; avatar images cached_network_image with resize; dedupe map for student joins; no rebuild of full detail on child-card state change (RepaintBoundary).

12. Proposals flagged to the team

  1. Backend: add GET /parents/me (or userId filter) + link-populated children endpoint — unblocks the entire parent self-service surface (OQ-1).
  2. Backend: unique index on (tenantId, studentId, parentId) + 409 on duplicate link; enforce findById result in linkStudentParent (OQ-2/OQ-3).
  3. Backend: link PATCH endpoint (or switch-primary endpoint) to replace the delete+recreate workaround (OQ-4); consider single-primary rule (OQ-5).
  4. Backend: @IsEnum(RelationshipType) in LinkParentDto (OQ-6).
  5. Backend/RBAC: seed parent.* permissions (04-Modules/Parents.md:64-70) and decorate endpoints (OQ-8).
  6. Analytics events (parents.*) wait AnalyticsService (00-shared/10 §8).