- 配置 edge-to-edge 沉浸式状态栏与刘海屏适配 - 新增百度地图权限、AK 配置及定位权限说明 - 接入 MethodChannel 实现返回键退后台热启动优化 - 配置自定义签名并更新构建逻辑 - 完善 token 刷新与鉴权失效统一处理 - 新增地图页与截图页路由 - 修复返回键拦截导致的 PopScope 失效问题
238 lines
8.6 KiB
Dart
238 lines
8.6 KiB
Dart
import 'package:cupertino_ui/cupertino_ui.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
|
||
import '../../../core/widgets/feature_ui.dart';
|
||
import '../../../l10n/app_localizations.dart';
|
||
|
||
/// 管理 Tab 内容(特性模块:messages / presentation)。
|
||
///
|
||
/// 对应移动端设计稿的「管理」页:顶部横版菜单(相册/闹钟/联系人/应用 …)
|
||
/// 可点击进入对应子页面,下方为消息列表。底部 Tab 文案为「管理」。
|
||
class MessagesTab extends ConsumerWidget {
|
||
const MessagesTab({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context, WidgetRef ref) {
|
||
final l10n = AppLocalizations.of(context);
|
||
|
||
final menus = [
|
||
_Menu(emoji: '🖼️', color: AppColors.blue, label: l10n.menuAlbum, route: '/album'),
|
||
_Menu(emoji: '⏰', color: AppColors.orange, label: l10n.menuAlarm, route: '/alarm'),
|
||
_Menu(emoji: '👥', color: AppColors.green, label: l10n.menuContacts, route: '/contacts'),
|
||
_Menu(emoji: '🧩', color: AppColors.purple, label: l10n.menuApps, route: '/apps'),
|
||
_Menu(emoji: '📁', color: AppColors.teal, label: l10n.menuFiles, route: '/apps'),
|
||
_Menu(emoji: '➕', color: AppColors.gray, label: l10n.menuMore, route: null),
|
||
];
|
||
|
||
final messages = [
|
||
_Message(emoji: '📱', color: AppColors.green, title: l10n.msgOnline,
|
||
preview: '我的平板 Pro 已连接办公室 WiFi', time: '09:30', unread: true, badge: 1),
|
||
_Message(emoji: '🖼️', color: AppColors.blue, title: l10n.msgScreenshot,
|
||
preview: '收到 3 张新截图,已存入相册', time: '昨天', unread: false),
|
||
_Message(emoji: '🛠️', color: AppColors.purple, title: l10n.msgRemote,
|
||
preview: '李工请求对平板进行远程协助', time: '昨天', unread: true),
|
||
_Message(emoji: '🔋', color: AppColors.red, title: l10n.msgBattery,
|
||
preview: '平板电量低于 20%,请尽快充电', time: '周一', unread: false),
|
||
_Message(emoji: '🔄', color: AppColors.teal, title: l10n.msgUpdate,
|
||
preview: '平板管家 v3.2.1 已更新完成', time: '周一', unread: false),
|
||
];
|
||
|
||
return CupertinoPageScaffold(
|
||
navigationBar: CupertinoNavigationBar(
|
||
middle: Text(l10n.manageTitle),
|
||
trailing: Container(
|
||
width: 38,
|
||
height: 38,
|
||
decoration: BoxDecoration(
|
||
color: CupertinoColors.white,
|
||
borderRadius: BorderRadius.circular(12),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: CupertinoColors.black.withValues(alpha: 0.06),
|
||
blurRadius: 8,
|
||
offset: const Offset(0, 2),
|
||
),
|
||
],
|
||
),
|
||
child: Stack(
|
||
children: [
|
||
const Center(
|
||
child:
|
||
Icon(CupertinoIcons.bell, size: 18, color: AppColors.ink)),
|
||
Positioned(
|
||
top: 5,
|
||
right: 7,
|
||
child: Container(
|
||
width: 8,
|
||
height: 8,
|
||
decoration: const BoxDecoration(
|
||
color: AppColors.red, shape: BoxShape.circle),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
child: SafeArea(
|
||
child: ListView(
|
||
padding: const EdgeInsets.only(bottom: 96),
|
||
children: [
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(18, 10, 18, 4),
|
||
child: Text(l10n.manageSubtitle,
|
||
style: const TextStyle(fontSize: 12, color: AppColors.sub)),
|
||
),
|
||
// 横版菜单
|
||
SizedBox(
|
||
height: 96,
|
||
child: ListView.separated(
|
||
scrollDirection: Axis.horizontal,
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
separatorBuilder: (_, _) => const SizedBox(width: 12),
|
||
itemCount: menus.length,
|
||
itemBuilder: (_, i) {
|
||
final m = menus[i];
|
||
return Container(
|
||
width: 80,
|
||
decoration: BoxDecoration(
|
||
color: CupertinoColors.white,
|
||
borderRadius: BorderRadius.circular(16),
|
||
boxShadow: const [
|
||
BoxShadow(color: Color(0x14141E3C), blurRadius: 14, offset: Offset(0, 4)),
|
||
],
|
||
),
|
||
child: IconTile(
|
||
emoji: m.emoji,
|
||
color: m.color,
|
||
label: m.label,
|
||
size: 44,
|
||
iconSize: 22,
|
||
onTap: m.route == null ? null : () => context.push(m.route!),
|
||
),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
SectionTitle(l10n.tabMessages, action: l10n.msgAllRead),
|
||
CardBox(
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
children: [
|
||
for (var i = 0; i < messages.length; i++) ...[
|
||
if (i > 0)
|
||
const Padding(
|
||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||
child: AppDivider(),
|
||
),
|
||
_messageRow(messages[i]),
|
||
],
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _messageRow(_Message m) => Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||
child: Row(
|
||
children: [
|
||
Stack(
|
||
clipBehavior: Clip.none,
|
||
children: [
|
||
Container(
|
||
width: 44,
|
||
height: 44,
|
||
decoration: BoxDecoration(
|
||
color: m.color,
|
||
borderRadius: BorderRadius.circular(14),
|
||
),
|
||
child: Center(child: Text(m.emoji, style: const TextStyle(fontSize: 19))),
|
||
),
|
||
if (m.badge != null)
|
||
Positioned(
|
||
top: -4,
|
||
right: -4,
|
||
child: Container(
|
||
constraints: const BoxConstraints(minWidth: 17),
|
||
height: 17,
|
||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||
decoration: BoxDecoration(
|
||
color: AppColors.red,
|
||
borderRadius: BorderRadius.circular(9),
|
||
),
|
||
child: Center(
|
||
child: Text('${m.badge}',
|
||
style: const TextStyle(fontSize: 10, color: CupertinoColors.white)),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(width: 12),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Row(
|
||
children: [
|
||
Text(m.title,
|
||
style: const TextStyle(fontSize: 14.5, fontWeight: FontWeight.w600, color: AppColors.ink)),
|
||
const Spacer(),
|
||
Text(m.time, style: const TextStyle(fontSize: 11, color: AppColors.sub)),
|
||
],
|
||
),
|
||
const SizedBox(height: 3),
|
||
Text(m.preview,
|
||
style: const TextStyle(fontSize: 12.5, color: AppColors.sub),
|
||
maxLines: 1,
|
||
overflow: TextOverflow.ellipsis),
|
||
],
|
||
),
|
||
),
|
||
if (m.unread)
|
||
const Padding(
|
||
padding: EdgeInsets.only(left: 8),
|
||
child: SizedBox(
|
||
width: 9,
|
||
height: 9,
|
||
child: DecoratedBox(
|
||
decoration: BoxDecoration(color: AppColors.red, shape: BoxShape.circle),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
class _Menu {
|
||
const _Menu({required this.emoji, required this.color, required this.label, this.route});
|
||
final String emoji;
|
||
final Color color;
|
||
final String label;
|
||
final String? route;
|
||
}
|
||
|
||
class _Message {
|
||
const _Message({
|
||
required this.emoji,
|
||
required this.color,
|
||
required this.title,
|
||
required this.preview,
|
||
required this.time,
|
||
required this.unread,
|
||
this.badge,
|
||
});
|
||
final String emoji;
|
||
final Color color;
|
||
final String title;
|
||
final String preview;
|
||
final String time;
|
||
final bool unread;
|
||
final int? badge;
|
||
}
|