# Android Nav Restructure & Projects Staleness Fix Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Replace the Projects bottom-nav tab with a "More" bottom sheet giving access to Projects, News, and Calendar; fix stale task data when returning from task edit. **Architecture:** The shell's `_tabs` list shrinks from 4 to 3 entries; the 4th nav destination ("More") is intercepted in `onDestinationSelected` to show a `showModalBottomSheet` instead of navigating. Projects moves from a ShellRoute to a top-level push route. News and Calendar are added as stub push-routes. `_tabIndex` maps the three overflow paths to index 3 so "More" highlights correctly. The staleness fix moves the task-tap callback out of the stateless `_TaskRow` widget into the parent `ConsumerState` where `ref` is available, using `.then()` to invalidate after pop. **Tech Stack:** Flutter, GoRouter, Riverpod, Material 3 --- ## Files | File | Action | What changes | |------|--------|-------------| | `lib/core/constants.dart` | Modify | Add `news` and `calendar` route constants | | `lib/app.dart` | Modify | Remove Projects from ShellRoute, add 3 push routes, shrink `_tabs`, add `_showMoreSheet`, update `_tabIndex`, update both nav widgets | | `lib/screens/library/project_tasks_screen.dart` | Modify | Add `onTap: VoidCallback` to `_TaskRow`; add `_openTask` method to state; pass callback at both call sites | --- ### Task 1: Add route constants **Files:** - Modify: `lib/core/constants.dart` - [ ] **Step 1: Add `news` and `calendar` to `Routes`** Open `lib/core/constants.dart`. The file currently ends with: ```dart abstract class Routes { static const splash = '/'; static const setup = '/setup'; static const login = '/login'; static const notes = '/notes'; static const noteDetail = '/notes/:id'; static const noteEdit = '/notes/:id/edit'; static const noteNew = '/notes/new'; static const tasks = '/tasks'; static const taskNew = '/tasks/new'; static const taskEdit = '/tasks/:id/edit'; static const knowledge = '/knowledge'; static const projects = '/projects'; static const projectEdit = '/projects/:id/edit'; static const conversations = '/chat'; static const chat = '/chat/:id'; static const quickCapture = '/quick-capture'; static const settings = '/settings'; static const briefing = '/briefing'; static const projectTasks = '/projects/:id/tasks'; } ``` Add two constants after `briefing`: ```dart abstract class Routes { static const splash = '/'; static const setup = '/setup'; static const login = '/login'; static const notes = '/notes'; static const noteDetail = '/notes/:id'; static const noteEdit = '/notes/:id/edit'; static const noteNew = '/notes/new'; static const tasks = '/tasks'; static const taskNew = '/tasks/new'; static const taskEdit = '/tasks/:id/edit'; static const knowledge = '/knowledge'; static const projects = '/projects'; static const projectEdit = '/projects/:id/edit'; static const conversations = '/chat'; static const chat = '/chat/:id'; static const quickCapture = '/quick-capture'; static const settings = '/settings'; static const briefing = '/briefing'; static const news = '/news'; static const calendar = '/calendar'; static const projectTasks = '/projects/:id/tasks'; } ``` - [ ] **Step 2: Verify no analysis errors** ```bash cd /home/bvandeusen/Nextcloud/Projects/fabled_app flutter analyze lib/core/constants.dart ``` Expected: `No issues found!` --- ### Task 2: Restructure shell and add routes in `app.dart` **Files:** - Modify: `lib/app.dart` This task has several sub-steps. Make them all before running analyze. - [ ] **Step 1: Move Projects out of ShellRoute, add three push routes** Find the `ShellRoute` block (currently lines ~142–162): ```dart ShellRoute( builder: (context, state, child) => _Shell(child: child), routes: [ GoRoute( path: Routes.briefing, builder: (_, _) => const BriefingScreen(), ), GoRoute( path: Routes.knowledge, builder: (_, _) => const KnowledgeScreen(), ), GoRoute( path: Routes.conversations, builder: (_, _) => const ConversationsTabScreen(), ), GoRoute( path: Routes.projects, builder: (_, _) => const ProjectsScreen(), ), ], ), ``` Replace with (Projects removed from shell; three new top-level routes added after the closing `],` of the ShellRoute): ```dart ShellRoute( builder: (context, state, child) => _Shell(child: child), routes: [ GoRoute( path: Routes.briefing, builder: (_, _) => const BriefingScreen(), ), GoRoute( path: Routes.knowledge, builder: (_, _) => const KnowledgeScreen(), ), GoRoute( path: Routes.conversations, builder: (_, _) => const ConversationsTabScreen(), ), ], ), GoRoute( path: Routes.projects, builder: (_, _) => const ProjectsScreen(), ), GoRoute( path: Routes.news, builder: (_, _) => Scaffold( appBar: AppBar(title: const Text('News')), body: const Center(child: Text('News — coming soon')), ), ), GoRoute( path: Routes.calendar, builder: (_, _) => Scaffold( appBar: AppBar(title: const Text('Calendar')), body: const Center(child: Text('Calendar — coming soon')), ), ), ``` - [ ] **Step 2: Shrink `_tabs` from 4 to 3 entries** Find in `_ShellState`: ```dart static const _tabs = [ Routes.briefing, Routes.knowledge, Routes.conversations, Routes.projects, ]; ``` Replace with: ```dart static const _tabs = [ Routes.briefing, Routes.knowledge, Routes.conversations, ]; ``` - [ ] **Step 3: Update `_tabIndex` to map overflow routes to index 3** Find: ```dart int _tabIndex(String location) { for (var i = 0; i < _tabs.length; i++) { if (location.startsWith(_tabs[i])) return i; } return 0; } ``` Replace with: ```dart int _tabIndex(String location) { for (var i = 0; i < _tabs.length; i++) { if (location.startsWith(_tabs[i])) return i; } if (location.startsWith(Routes.projects) || location.startsWith(Routes.news) || location.startsWith(Routes.calendar)) return 3; return 0; } ``` - [ ] **Step 4: Add `_showMoreSheet` method to `_ShellState`** Add this method anywhere in `_ShellState`, e.g. just before `build`: ```dart void _showMoreSheet(BuildContext context) { showModalBottomSheet( context: context, builder: (_) => SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.folder_outlined), title: const Text('Projects'), onTap: () { Navigator.pop(context); context.push(Routes.projects); }, ), ListTile( leading: const Icon(Icons.newspaper_outlined), title: const Text('News'), onTap: () { Navigator.pop(context); context.push(Routes.news); }, ), ListTile( leading: const Icon(Icons.calendar_month_outlined), title: const Text('Calendar'), onTap: () { Navigator.pop(context); context.push(Routes.calendar); }, ), ], ), ), ); } ``` - [ ] **Step 5: Update `NavigationRail` — intercept index 3 and swap destination** Find in the wide-layout branch: ```dart NavigationRail( selectedIndex: index, onDestinationSelected: (i) => context.go(_tabs[i]), labelType: NavigationRailLabelType.all, destinations: const [ NavigationRailDestination( icon: Icon(Icons.wb_sunny_outlined), selectedIcon: Icon(Icons.wb_sunny), label: Text('Briefing'), ), NavigationRailDestination( icon: Icon(Icons.menu_book_outlined), selectedIcon: Icon(Icons.menu_book), label: Text('Knowledge'), ), NavigationRailDestination( icon: Icon(Icons.chat_bubble_outline), selectedIcon: Icon(Icons.chat_bubble), label: Text('Chat'), ), NavigationRailDestination( icon: Icon(Icons.folder_outlined), selectedIcon: Icon(Icons.folder), label: Text('Projects'), ), ], ), ``` Replace with: ```dart NavigationRail( selectedIndex: index, onDestinationSelected: (i) { if (i == 3) { _showMoreSheet(context); } else { context.go(_tabs[i]); } }, labelType: NavigationRailLabelType.all, destinations: const [ NavigationRailDestination( icon: Icon(Icons.wb_sunny_outlined), selectedIcon: Icon(Icons.wb_sunny), label: Text('Briefing'), ), NavigationRailDestination( icon: Icon(Icons.menu_book_outlined), selectedIcon: Icon(Icons.menu_book), label: Text('Knowledge'), ), NavigationRailDestination( icon: Icon(Icons.chat_bubble_outline), selectedIcon: Icon(Icons.chat_bubble), label: Text('Chat'), ), NavigationRailDestination( icon: Icon(Icons.more_horiz_outlined), selectedIcon: Icon(Icons.more_horiz), label: Text('More'), ), ], ), ``` - [ ] **Step 6: Update `NavigationBar` — intercept index 3 and swap destination** Find in the narrow-layout branch: ```dart bottomNavigationBar: NavigationBar( selectedIndex: index, onDestinationSelected: (i) => context.go(_tabs[i]), destinations: const [ NavigationDestination( icon: Icon(Icons.wb_sunny_outlined), selectedIcon: Icon(Icons.wb_sunny), label: 'Briefing', ), NavigationDestination( icon: Icon(Icons.menu_book_outlined), selectedIcon: Icon(Icons.menu_book), label: 'Knowledge', ), NavigationDestination( icon: Icon(Icons.chat_bubble_outline), selectedIcon: Icon(Icons.chat_bubble), label: 'Chat', ), NavigationDestination( icon: Icon(Icons.folder_outlined), selectedIcon: Icon(Icons.folder), label: 'Projects', ), ], ), ``` Replace with: ```dart bottomNavigationBar: NavigationBar( selectedIndex: index, onDestinationSelected: (i) { if (i == 3) { _showMoreSheet(context); } else { context.go(_tabs[i]); } }, destinations: const [ NavigationDestination( icon: Icon(Icons.wb_sunny_outlined), selectedIcon: Icon(Icons.wb_sunny), label: 'Briefing', ), NavigationDestination( icon: Icon(Icons.menu_book_outlined), selectedIcon: Icon(Icons.menu_book), label: 'Knowledge', ), NavigationDestination( icon: Icon(Icons.chat_bubble_outline), selectedIcon: Icon(Icons.chat_bubble), label: 'Chat', ), NavigationDestination( icon: Icon(Icons.more_horiz_outlined), selectedIcon: Icon(Icons.more_horiz), label: 'More', ), ], ), ``` - [ ] **Step 7: Verify no analysis errors** ```bash cd /home/bvandeusen/Nextcloud/Projects/fabled_app flutter analyze lib/app.dart lib/core/constants.dart ``` Expected: `No issues found!` - [ ] **Step 8: Commit** ```bash git add lib/core/constants.dart lib/app.dart git commit -m "feat: replace Projects tab with More bottom sheet (news/calendar stubs)" ``` --- ### Task 3: Fix stale tasks after returning from task edit **Files:** - Modify: `lib/screens/library/project_tasks_screen.dart` - [ ] **Step 1: Add `_openTask` method to `_ProjectTasksScreenState`** `_ProjectTasksScreenState` is a `ConsumerState` — it has `ref` and `context`. Find the class body (look for `_cycleStatus` method as a landmark) and add `_openTask` as a sibling method: ```dart void _openTask(int taskId) { context .push(Routes.taskEdit.replaceFirst(':id', '$taskId')) .then((_) => ref.invalidate(projectTasksProvider(widget.projectId))); } ``` - [ ] **Step 2: Add `onTap` parameter to `_TaskRow`** Find the `_TaskRow` class definition: ```dart class _TaskRow extends StatelessWidget { final Task task; final TaskStatus effectiveStatus; final VoidCallback onStatusTap; const _TaskRow({ required this.task, required this.effectiveStatus, required this.onStatusTap, }); ``` Replace with: ```dart class _TaskRow extends StatelessWidget { final Task task; final TaskStatus effectiveStatus; final VoidCallback onStatusTap; final VoidCallback onTap; const _TaskRow({ required this.task, required this.effectiveStatus, required this.onStatusTap, required this.onTap, }); ``` - [ ] **Step 3: Use the `onTap` callback in `InkWell`** Find inside `_TaskRow.build`: ```dart child: InkWell( onTap: () => context .push(Routes.taskEdit.replaceFirst(':id', '${task.id}')), ``` Replace with: ```dart child: InkWell( onTap: onTap, ``` - [ ] **Step 4: Pass `onTap` at both `_TaskRow` call sites** There are two places in `_buildBody` where `_TaskRow` is instantiated. Update both: **First call site (milestone tasks, around line 170):** ```dart return _TaskRow( task: task, effectiveStatus: _effectiveStatus(task), onStatusTap: () => _cycleStatus(task), onTap: () => _openTask(task.id), ); ``` **Second call site (unassigned tasks, around line 197):** ```dart return _TaskRow( task: task, effectiveStatus: _effectiveStatus(task), onStatusTap: () => _cycleStatus(task), onTap: () => _openTask(task.id), ); ``` - [ ] **Step 5: Verify no analysis errors** ```bash cd /home/bvandeusen/Nextcloud/Projects/fabled_app flutter analyze lib/screens/library/project_tasks_screen.dart ``` Expected: `No issues found!` - [ ] **Step 6: Commit** ```bash git add lib/screens/library/project_tasks_screen.dart git commit -m "fix: invalidate project tasks on return from task edit screen" ``` --- ### Task 4: Final check - [ ] **Step 1: Full analyze** ```bash cd /home/bvandeusen/Nextcloud/Projects/fabled_app flutter analyze ``` Expected: `No issues found!`