import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import '../core/auth/auth_state.dart'; import '../core/config/license_copy.dart'; import '../core/theme/app_chrome.g.dart'; import '../providers/connectivity_provider.dart'; import '../providers/license_provider.dart'; import '../providers/update_provider.dart'; /// 底部状态栏(原型 .statusbar,主题无关 chrome 条)。 /// 左:门店编号 / 操作员 / 登录时间 / 当前时间(每秒走字) 右:连接 / 版本 / 授权。 class AppStatusBar extends ConsumerStatefulWidget { final String loginTime; const AppStatusBar({super.key, required this.loginTime}); /// 时钟种子(可测试缝):golden 测试覆盖为固定时间以确定化;走字默认关。 static DateTime Function() clock = DateTime.now; /// 是否每秒走字(golden 测试关掉,避免时间变动造成像素抖动 / pending timer)。 static bool ticking = true; @override ConsumerState createState() => _AppStatusBarState(); } class _AppStatusBarState extends ConsumerState { Timer? _timer; late String _now; @override void initState() { super.initState(); _now = DateFormat('HH:mm:ss').format(AppStatusBar.clock()); if (AppStatusBar.ticking) { _timer = Timer.periodic(const Duration(seconds: 1), (_) { if (mounted) { setState(() => _now = DateFormat('HH:mm:ss').format(AppStatusBar.clock())); } }); } } @override void dispose() { _timer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { final user = ref.watch(authStateProvider).user; final isOnline = ref.watch(connectivityProvider); final version = ref.watch(appVersionProvider).valueOrNull ?? 'v1.0.0'; final lic = ref.watch(licenseProvider).valueOrNull; return Container( height: 34, color: AppChrome.statusbarBg, padding: const EdgeInsets.symmetric(horizontal: 6), child: Row( children: [ // 左组 _item(icon: Icons.storefront_outlined, strong: user?.shopNo ?? '—'), _div(), _item( icon: Icons.person_outline, text: (user == null || user.realName.isEmpty) ? (user?.username ?? '—') : user.realName), _div(), _item(icon: Icons.login, text: '登录 ', strong: widget.loginTime), _div(), _item(icon: Icons.schedule, text: '当前 ', strong: _now), const Spacer(), // 右组 _item( icon: isOnline ? Icons.cloud_done_outlined : Icons.cloud_off_outlined, text: isOnline ? '已连接' : '连接已断开', ok: isOnline, ), _div(), _item(icon: Icons.sell_outlined, strong: version, ok: true), _div(), _licenseItem(lic), ], ), ); } Widget _div() => Container( width: 1, height: 14, color: AppChrome.statusbarDiv, margin: const EdgeInsets.symmetric(horizontal: 0), ); // 单项:图标 + 普通文字 + 强调文字(mono 强调色)。ok=true 用 statusbar-ok 绿。 Widget _item({ required IconData icon, String? text, String? strong, bool ok = false, Color? color, }) { final c = color ?? (ok ? AppChrome.statusbarOk : AppChrome.statusbarFg); final iconC = ok ? AppChrome.statusbarOk : AppChrome.statusbarMuted; return Padding( padding: const EdgeInsets.symmetric(horizontal: 13), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 13, color: iconC), const SizedBox(width: 6), if (text != null) Text(text, style: TextStyle(fontSize: 11, color: c)), if (strong != null) Text( strong, style: TextStyle( fontSize: 11, color: ok ? AppChrome.statusbarOk : AppChrome.statusbarStrong, fontWeight: FontWeight.w600, ), ), ], ), ); } Widget _licenseItem(LicenseInfo? lic) { if (lic == null) { return _item(icon: Icons.verified_user_outlined, text: '授权', ok: true); } final color = LicenseCopy.phaseColor(lic.phase); final icon = LicenseCopy.phaseIcon(lic.phase); final date = lic.expiresAt == null ? '' : DateFormat('yyyy-MM-dd').format(lic.expiresAt!); final text = LicenseCopy.statusBar(lic, date); final normal = lic.phase == 'normal'; return Padding( padding: const EdgeInsets.symmetric(horizontal: 13), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 13, color: normal ? AppChrome.statusbarOk : color), const SizedBox(width: 6), Text( text, style: TextStyle( fontSize: 11, color: normal ? AppChrome.statusbarOk : color, fontWeight: FontWeight.w600, ), ), ], ), ); } }