feat(client): 登录历史下拉、macOS 兼容修复、UI 优化

- 替换 flutter_secure_storage 为 shared_preferences,解决 macOS Keychain 签名报错
- 登录页门店编号/用户名支持历史下拉(最多5条,LRU 淘汰),使用 Overlay + CompositedTransformFollower 实现浮层
- logout 仅删除 auth token,保留登录历史
- AppShell 时钟抽为独立 _ClockWidget,避免每秒 setState 导致 TabBarView 叠影
- ShellRoute 子路由改用 NoTransitionPage,消除切换页面时的过渡动画叠影
- 新增 auth_state_test、auth_repository_test、login_screen_test 共 14 个单元/Widget 测试

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-04-05 01:21:40 +08:00
parent cc687ae0c3
commit 37112d6599
15 changed files with 895 additions and 407 deletions
+32 -23
View File
@@ -16,8 +16,6 @@ class AppShell extends ConsumerStatefulWidget {
class _AppShellState extends ConsumerState<AppShell> {
bool _sidebarExpanded = true;
late Timer _timer;
late String _currentTime;
final String _loginTime =
DateFormat('HH:mm:ss').format(DateTime.now());
@@ -34,24 +32,6 @@ class _AppShellState extends ConsumerState<AppShell> {
_NavItem(icon: Icons.settings, label: '系统设置', path: '/settings'),
];
@override
void initState() {
super.initState();
_currentTime = DateFormat('HH:mm:ss').format(DateTime.now());
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
if (mounted) {
setState(() =>
_currentTime = DateFormat('HH:mm:ss').format(DateTime.now()));
}
});
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
final user = ref.watch(authStateProvider).user;
@@ -192,9 +172,7 @@ class _AppShellState extends ConsumerState<AppShell> {
text: '登录时间:$_loginTime'),
const _StatusDivider(),
],
_StatusItem(
icon: Icons.access_time,
text: '当前时间:$_currentTime'),
const _ClockWidget(),
const Spacer(),
const _StatusItem(
icon: Icons.info_outline,
@@ -325,3 +303,34 @@ class _StatusDivider extends StatelessWidget {
);
}
}
class _ClockWidget extends StatefulWidget {
const _ClockWidget();
@override
State<_ClockWidget> createState() => _ClockWidgetState();
}
class _ClockWidgetState extends State<_ClockWidget> {
late Timer _timer;
late String _time;
@override
void initState() {
super.initState();
_time = DateFormat('HH:mm:ss').format(DateTime.now());
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
if (mounted) setState(() => _time = DateFormat('HH:mm:ss').format(DateTime.now()));
});
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return _StatusItem(icon: Icons.access_time, text: '当前时间:$_time');
}
}