fix: 主页的系统返回手势不再退回登录页

PopScope 的 canPop 写的是 !_loggingOut,平时即为 true,所以原生返回手势会
直接把 HomePage 弹出、落回 Login Page;同时因为 didPop==true,回调里的
退登分支根本不会执行 —— 一个原因同时造成「回到登录页」和「账号没退出」,
留下一个会话仍在线的半退出状态。

改为 canPop:false 把根路由锁死在主页,返回意图交给 _exitApp:非 iOS 平台
走 SystemNavigator.pop 结束应用(iOS 不允许程序化退出,根路由也没有返回手势)。
AppBar 的退出按钮与风险操作完成后的 onExitToTitle 仍走 _performLogoutAndExit,
那两处是明确要回主标题。

新增回归用例:把 HomePage push 在占位根路由之上,触发 handlePopRoute,
断言 HomePage 仍在栈上且调用了 SystemNavigator.pop。
This commit is contained in:
Cookiues committed 2026-09-26 01:09:13 +08:00
1 parent cb50426a61
commit 6b2efaebba
2 files changed
+63 -4

No files matched your search

+19 -4
View File
@@ -1,4 +1,7 @@
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show SystemNavigator;
import '../config/responsive.dart';
import '../config/strings.dart';
@@ -169,6 +172,9 @@ class _HomePageState extends State<HomePage> {
await _session.logoutGame(userId: widget.userId);
}
/// 退登并回到主标题页。供 AppBar 的退出按钮与风险操作完成后的
/// onExitToTitle 使用——这两处都是「明确要回标题」,与主页上的
/// 系统返回手势不同(后者见 [_exitApp])。
Future<void> _performLogoutAndExit() async {
setState(() => _loggingOut = true);
if (_session.gameLogin != null) {
@@ -184,6 +190,13 @@ class _HomePageState extends State<HomePage> {
}
}
/// 主页上的原生返回手势 = 结束应用。
void _exitApp() {
// iOS 不允许程序化退出,且根路由本来也没有返回手势。
if (Platform.isIOS) return;
SystemNavigator.pop();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
@@ -192,11 +205,13 @@ class _HomePageState extends State<HomePage> {
listenable: _session,
builder: (context, _) {
return PopScope(
canPop: !_loggingOut,
// 根路由锁死在主页。之前 canPop 为 !_loggingOut(平时即 true),系统返回
// 手势会直接把 HomePage 弹掉、落回 Login Page,而且因为 didPop==true,
// 回调里的退登根本不会执行——既回错页面又没退登。
canPop: false,
onPopInvokedWithResult: (didPop, _) {
if (!didPop && !_loggingOut) {
_performLogoutAndExit();
}
if (didPop || _loggingOut) return;
_exitApp();
},
child: Scaffold(
appBar: AppBar(
+44
View File
@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:ichino/pages/home_page.dart';
/// 主页上的系统返回手势不能把 HomePage 弹掉(那会落回 Login Page,
/// 而且退登逻辑因为 didPop==true 根本不会跑)。
void main() {
testWidgets('主页收到系统返回时结束应用而不是退回上一页', (tester) async {
final calls = <String>[];
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(SystemChannels.platform, (call) async {
calls.add(call.method);
return null;
});
addTearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(SystemChannels.platform, null);
});
await tester.pumpWidget(
const MaterialApp(home: Text('login-root')),
);
final navigator = tester.state<NavigatorState>(find.byType(Navigator));
navigator.push(
MaterialPageRoute(builder: (_) => const HomePage(userId: 1, token: 't')),
);
await tester.pumpAndSettle();
expect(find.text('login-root'), findsNothing);
expect(find.byType(HomePage), findsOneWidget);
await tester.binding.handlePopRoute();
await tester.pumpAndSettle();
// HomePage 仍在栈上:根路由被锁死。
expect(find.byType(HomePage), findsOneWidget);
expect(find.text('login-root'), findsNothing);
// 非 iOS 上应当走 SystemNavigator.pop 结束应用。
expect(calls, contains('SystemNavigator.pop'));
});
}