Files
ttstd_family_care/lib/features/messages/presentation/messages_tab.dart
TongTongStudio 06cdc77172 feat(android): 支持沉浸式状态栏、地图定位与自定义签名
- 配置 edge-to-edge 沉浸式状态栏与刘海屏适配
- 新增百度地图权限、AK 配置及定位权限说明
- 接入 MethodChannel 实现返回键退后台热启动优化
- 配置自定义签名并更新构建逻辑
- 完善 token 刷新与鉴权失效统一处理
- 新增地图页与截图页路由
- 修复返回键拦截导致的 PopScope 失效问题
2026-08-19 03:43:12 +08:00

238 lines
8.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}