From cbbed29a75cab3ec2d3f7bedbce0897aade880fb Mon Sep 17 00:00:00 2001 From: qzl Date: Fri, 20 Mar 2026 15:20:29 +0800 Subject: [PATCH] feat: enforce hierarchical back navigation and home-only exit --- apps/lib/core/router/app_router.dart | 12 ++++++++---- .../calendar/ui/screens/calendar_dayweek_screen.dart | 4 ++-- .../calendar/ui/screens/calendar_month_screen.dart | 4 ++-- .../home/ui/navigation/home_return_policy.dart | 6 ++++++ .../settings/ui/screens/settings_screen.dart | 3 ++- .../todo/ui/screens/todo_quadrants_screen.dart | 4 ++-- .../home/ui/navigation/home_return_policy_test.dart | 9 +++++++++ 7 files changed, 31 insertions(+), 11 deletions(-) diff --git a/apps/lib/core/router/app_router.dart b/apps/lib/core/router/app_router.dart index 7ad6485..75aca49 100644 --- a/apps/lib/core/router/app_router.dart +++ b/apps/lib/core/router/app_router.dart @@ -26,15 +26,19 @@ import '../../features/settings/ui/screens/features_screen.dart'; import '../../features/settings/ui/screens/memory_screen.dart'; import '../../features/settings/ui/screens/edit_profile_screen.dart'; -final _protectedRoutes = [ +final _homeSecondLevelRoutes = [ AppRoutes.homeMain, - AppRoutes.contactsList, - AppRoutes.contactsAdd, AppRoutes.calendarDayWeek, AppRoutes.calendarMonth, - '/calendar/events', AppRoutes.todoList, AppRoutes.settingsMain, +]; + +final _protectedRoutes = [ + ..._homeSecondLevelRoutes, + AppRoutes.contactsList, + AppRoutes.contactsAdd, + '/calendar/events', AppRoutes.settingsFeatures, AppRoutes.settingsMemory, AppRoutes.settingsEditProfile, diff --git a/apps/lib/features/calendar/ui/screens/calendar_dayweek_screen.dart b/apps/lib/features/calendar/ui/screens/calendar_dayweek_screen.dart index 0e52cc9..4b4da5d 100644 --- a/apps/lib/features/calendar/ui/screens/calendar_dayweek_screen.dart +++ b/apps/lib/features/calendar/ui/screens/calendar_dayweek_screen.dart @@ -119,7 +119,7 @@ class _CalendarDayWeekScreenState extends State canPop: false, onPopInvokedWithResult: (didPop, result) { if (!didPop) { - returnToHomePreserveState(context); + returnToHomePreserveState(context, forceGoHome: true); } }, child: SafeArea( @@ -702,7 +702,7 @@ class _CalendarDayWeekScreenState extends State _calendarManager.setViewType(CalendarViewType.day); context.push(AppRoutes.calendarMonth); }, - onHomeTap: () => returnToHomePreserveState(context), + onHomeTap: () => returnToHomePreserveState(context, forceGoHome: true), ); } } diff --git a/apps/lib/features/calendar/ui/screens/calendar_month_screen.dart b/apps/lib/features/calendar/ui/screens/calendar_month_screen.dart index bf6355b..c4dab8a 100644 --- a/apps/lib/features/calendar/ui/screens/calendar_month_screen.dart +++ b/apps/lib/features/calendar/ui/screens/calendar_month_screen.dart @@ -105,7 +105,7 @@ class _CalendarMonthScreenState extends State canPop: false, onPopInvokedWithResult: (didPop, result) { if (!didPop) { - returnToHomePreserveState(context); + returnToHomePreserveState(context, forceGoHome: true); } }, child: SafeArea( @@ -522,7 +522,7 @@ class _CalendarMonthScreenState extends State context.push(AppRoutes.todoList); }, onCalendarTap: () {}, - onHomeTap: () => returnToHomePreserveState(context), + onHomeTap: () => returnToHomePreserveState(context, forceGoHome: true), ); } } diff --git a/apps/lib/features/home/ui/navigation/home_return_policy.dart b/apps/lib/features/home/ui/navigation/home_return_policy.dart index 8da767d..dd47128 100644 --- a/apps/lib/features/home/ui/navigation/home_return_policy.dart +++ b/apps/lib/features/home/ui/navigation/home_return_policy.dart @@ -8,7 +8,11 @@ enum HomeReturnAction { pop, goHome } HomeReturnAction resolveHomeReturnAction({ required bool canPop, required bool isAuthEntry, + bool forceGoHome = false, }) { + if (forceGoHome) { + return HomeReturnAction.goHome; + } if (isAuthEntry) { return HomeReturnAction.goHome; } @@ -21,10 +25,12 @@ HomeReturnAction resolveHomeReturnAction({ void returnToHomePreserveState( BuildContext context, { bool isAuthEntry = false, + bool forceGoHome = false, }) { final action = resolveHomeReturnAction( canPop: context.canPop(), isAuthEntry: isAuthEntry, + forceGoHome: forceGoHome, ); switch (action) { case HomeReturnAction.pop: diff --git a/apps/lib/features/settings/ui/screens/settings_screen.dart b/apps/lib/features/settings/ui/screens/settings_screen.dart index f645069..febb69e 100644 --- a/apps/lib/features/settings/ui/screens/settings_screen.dart +++ b/apps/lib/features/settings/ui/screens/settings_screen.dart @@ -20,6 +20,7 @@ import 'package:social_app/features/settings/data/settings_api.dart'; import 'package:social_app/features/settings/data/services/settings_user_cache.dart'; import 'package:social_app/features/users/data/models/user_response.dart'; import 'package:social_app/features/users/data/users_api.dart'; +import 'package:social_app/features/home/ui/navigation/home_return_policy.dart'; import '../widgets/settings_page_scaffold.dart'; const settingsProfileEditButtonKey = ValueKey('settings_profile_edit_button'); @@ -90,7 +91,7 @@ class _SettingsScreenState extends State { Widget build(BuildContext context) { return SettingsPageScaffold( title: '设置', - onBack: () => context.pop(), + onBack: () => returnToHomePreserveState(context, forceGoHome: true), body: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ diff --git a/apps/lib/features/todo/ui/screens/todo_quadrants_screen.dart b/apps/lib/features/todo/ui/screens/todo_quadrants_screen.dart index 165418d..23ddf6b 100644 --- a/apps/lib/features/todo/ui/screens/todo_quadrants_screen.dart +++ b/apps/lib/features/todo/ui/screens/todo_quadrants_screen.dart @@ -298,7 +298,7 @@ class _TodoQuadrantsScreenState extends State { canPop: false, onPopInvokedWithResult: (didPop, result) { if (!didPop) { - returnToHomePreserveState(context); + returnToHomePreserveState(context, forceGoHome: true); } }, child: SafeArea( @@ -563,7 +563,7 @@ class _TodoQuadrantsScreenState extends State { context.push('${AppRoutes.calendarDayWeek}?date=$dateStr'); } }, - onHomeTap: () => returnToHomePreserveState(context), + onHomeTap: () => returnToHomePreserveState(context, forceGoHome: true), ); } } diff --git a/apps/test/features/home/ui/navigation/home_return_policy_test.dart b/apps/test/features/home/ui/navigation/home_return_policy_test.dart index 7f06678..46ddfa8 100644 --- a/apps/test/features/home/ui/navigation/home_return_policy_test.dart +++ b/apps/test/features/home/ui/navigation/home_return_policy_test.dart @@ -3,6 +3,15 @@ import 'package:social_app/features/home/ui/navigation/home_return_policy.dart'; void main() { group('resolveHomeReturnAction', () { + test('second-level pages should return to home instead of exiting app', () { + final action = resolveHomeReturnAction( + canPop: false, + isAuthEntry: false, + forceGoHome: true, + ); + expect(action, HomeReturnAction.goHome); + }); + test('business route with back stack prefers pop', () { final action = resolveHomeReturnAction(canPop: true, isAuthEntry: false); expect(action, HomeReturnAction.pop);