feat: enforce hierarchical back navigation and home-only exit

This commit is contained in:
qzl
2026-03-20 15:20:29 +08:00
parent 19a2cd451d
commit cbbed29a75
7 changed files with 31 additions and 11 deletions
+8 -4
View File
@@ -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/memory_screen.dart';
import '../../features/settings/ui/screens/edit_profile_screen.dart'; import '../../features/settings/ui/screens/edit_profile_screen.dart';
final _protectedRoutes = [ final _homeSecondLevelRoutes = [
AppRoutes.homeMain, AppRoutes.homeMain,
AppRoutes.contactsList,
AppRoutes.contactsAdd,
AppRoutes.calendarDayWeek, AppRoutes.calendarDayWeek,
AppRoutes.calendarMonth, AppRoutes.calendarMonth,
'/calendar/events',
AppRoutes.todoList, AppRoutes.todoList,
AppRoutes.settingsMain, AppRoutes.settingsMain,
];
final _protectedRoutes = [
..._homeSecondLevelRoutes,
AppRoutes.contactsList,
AppRoutes.contactsAdd,
'/calendar/events',
AppRoutes.settingsFeatures, AppRoutes.settingsFeatures,
AppRoutes.settingsMemory, AppRoutes.settingsMemory,
AppRoutes.settingsEditProfile, AppRoutes.settingsEditProfile,
@@ -119,7 +119,7 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
canPop: false, canPop: false,
onPopInvokedWithResult: (didPop, result) { onPopInvokedWithResult: (didPop, result) {
if (!didPop) { if (!didPop) {
returnToHomePreserveState(context); returnToHomePreserveState(context, forceGoHome: true);
} }
}, },
child: SafeArea( child: SafeArea(
@@ -702,7 +702,7 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
_calendarManager.setViewType(CalendarViewType.day); _calendarManager.setViewType(CalendarViewType.day);
context.push(AppRoutes.calendarMonth); context.push(AppRoutes.calendarMonth);
}, },
onHomeTap: () => returnToHomePreserveState(context), onHomeTap: () => returnToHomePreserveState(context, forceGoHome: true),
); );
} }
} }
@@ -105,7 +105,7 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
canPop: false, canPop: false,
onPopInvokedWithResult: (didPop, result) { onPopInvokedWithResult: (didPop, result) {
if (!didPop) { if (!didPop) {
returnToHomePreserveState(context); returnToHomePreserveState(context, forceGoHome: true);
} }
}, },
child: SafeArea( child: SafeArea(
@@ -522,7 +522,7 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
context.push(AppRoutes.todoList); context.push(AppRoutes.todoList);
}, },
onCalendarTap: () {}, onCalendarTap: () {},
onHomeTap: () => returnToHomePreserveState(context), onHomeTap: () => returnToHomePreserveState(context, forceGoHome: true),
); );
} }
} }
@@ -8,7 +8,11 @@ enum HomeReturnAction { pop, goHome }
HomeReturnAction resolveHomeReturnAction({ HomeReturnAction resolveHomeReturnAction({
required bool canPop, required bool canPop,
required bool isAuthEntry, required bool isAuthEntry,
bool forceGoHome = false,
}) { }) {
if (forceGoHome) {
return HomeReturnAction.goHome;
}
if (isAuthEntry) { if (isAuthEntry) {
return HomeReturnAction.goHome; return HomeReturnAction.goHome;
} }
@@ -21,10 +25,12 @@ HomeReturnAction resolveHomeReturnAction({
void returnToHomePreserveState( void returnToHomePreserveState(
BuildContext context, { BuildContext context, {
bool isAuthEntry = false, bool isAuthEntry = false,
bool forceGoHome = false,
}) { }) {
final action = resolveHomeReturnAction( final action = resolveHomeReturnAction(
canPop: context.canPop(), canPop: context.canPop(),
isAuthEntry: isAuthEntry, isAuthEntry: isAuthEntry,
forceGoHome: forceGoHome,
); );
switch (action) { switch (action) {
case HomeReturnAction.pop: case HomeReturnAction.pop:
@@ -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/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/models/user_response.dart';
import 'package:social_app/features/users/data/users_api.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'; import '../widgets/settings_page_scaffold.dart';
const settingsProfileEditButtonKey = ValueKey('settings_profile_edit_button'); const settingsProfileEditButtonKey = ValueKey('settings_profile_edit_button');
@@ -90,7 +91,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SettingsPageScaffold( return SettingsPageScaffold(
title: '设置', title: '设置',
onBack: () => context.pop(), onBack: () => returnToHomePreserveState(context, forceGoHome: true),
body: Column( body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
@@ -298,7 +298,7 @@ class _TodoQuadrantsScreenState extends State<TodoQuadrantsScreen> {
canPop: false, canPop: false,
onPopInvokedWithResult: (didPop, result) { onPopInvokedWithResult: (didPop, result) {
if (!didPop) { if (!didPop) {
returnToHomePreserveState(context); returnToHomePreserveState(context, forceGoHome: true);
} }
}, },
child: SafeArea( child: SafeArea(
@@ -563,7 +563,7 @@ class _TodoQuadrantsScreenState extends State<TodoQuadrantsScreen> {
context.push('${AppRoutes.calendarDayWeek}?date=$dateStr'); context.push('${AppRoutes.calendarDayWeek}?date=$dateStr');
} }
}, },
onHomeTap: () => returnToHomePreserveState(context), onHomeTap: () => returnToHomePreserveState(context, forceGoHome: true),
); );
} }
} }
@@ -3,6 +3,15 @@ import 'package:social_app/features/home/ui/navigation/home_return_policy.dart';
void main() { void main() {
group('resolveHomeReturnAction', () { 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', () { test('business route with back stack prefers pop', () {
final action = resolveHomeReturnAction(canPop: true, isAuthEntry: false); final action = resolveHomeReturnAction(canPop: true, isAuthEntry: false);
expect(action, HomeReturnAction.pop); expect(action, HomeReturnAction.pop);