gcx/frontend/mobile/lib/features/message/presentation/pages/message_page.dart

217 lines
5.7 KiB
Dart

import 'package:flutter/material.dart';
import '../../../../app/i18n/app_localizations.dart';
import '../../../../app/theme/app_colors.dart';
import '../../../../app/theme/app_typography.dart';
import '../../../../app/theme/app_spacing.dart';
import '../../../../shared/widgets/empty_state.dart';
/// A8. 消息模块
///
/// 交易通知、系统公告、券到期提醒、价格提醒
/// 分类Tab + 消息详情
class MessagePage extends StatefulWidget {
const MessagePage({super.key});
@override
State<MessagePage> createState() => _MessagePageState();
}
class _MessagePageState extends State<MessagePage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 4, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(context.t('message.title')),
actions: [
TextButton(
onPressed: () {},
child: Text(context.t('message.markAllRead'), style: AppTypography.labelSmall.copyWith(
color: AppColors.primary,
)),
),
],
bottom: TabBar(
controller: _tabController,
tabs: [
Tab(text: context.t('common.all')),
Tab(text: context.t('message.tabTrade')),
Tab(text: context.t('message.tabExpiry')),
Tab(text: context.t('message.tabAnnouncement')),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
_buildMessageList(all: true),
_buildMessageList(type: MessageType.transaction),
_buildMessageList(type: MessageType.expiry),
_buildMessageList(type: MessageType.announcement),
],
),
);
}
Widget _buildMessageList({bool all = false, MessageType? type}) {
if (type == MessageType.announcement) {
return EmptyState.noMessages(context: context);
}
final messages = _mockMessages
.where((m) => all || m.type == type)
.toList();
return ListView.separated(
padding: const EdgeInsets.symmetric(vertical: 8),
itemCount: messages.length,
separatorBuilder: (_, __) => const Divider(indent: 76),
itemBuilder: (context, index) {
final msg = messages[index];
return _buildMessageItem(msg);
},
);
}
Widget _buildMessageItem(_MockMessage msg) {
return ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 4),
leading: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: _iconBgColor(msg.type),
shape: BoxShape.circle,
),
child: Icon(_iconData(msg.type), size: 22, color: _iconColor(msg.type)),
),
title: Row(
children: [
Expanded(
child: Text(msg.title, style: AppTypography.labelMedium.copyWith(
fontWeight: msg.isRead ? FontWeight.w400 : FontWeight.w600,
)),
),
Text(msg.time, style: AppTypography.caption),
],
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(
msg.body,
style: AppTypography.bodySmall,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
trailing: !msg.isRead
? Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
color: AppColors.primary,
shape: BoxShape.circle,
),
)
: null,
onTap: () {
Navigator.pushNamed(context, '/message/detail');
},
);
}
IconData _iconData(MessageType type) {
switch (type) {
case MessageType.transaction:
return Icons.swap_horiz_rounded;
case MessageType.expiry:
return Icons.access_time_rounded;
case MessageType.price:
return Icons.trending_up_rounded;
case MessageType.announcement:
return Icons.campaign_rounded;
}
}
Color _iconColor(MessageType type) {
switch (type) {
case MessageType.transaction:
return AppColors.primary;
case MessageType.expiry:
return AppColors.warning;
case MessageType.price:
return AppColors.success;
case MessageType.announcement:
return AppColors.info;
}
}
Color _iconBgColor(MessageType type) {
return _iconColor(type).withValues(alpha: 0.1);
}
}
enum MessageType { transaction, expiry, price, announcement }
class _MockMessage {
final String title;
final String body;
final String time;
final MessageType type;
final bool isRead;
const _MockMessage(this.title, this.body, this.time, this.type, this.isRead);
}
const _mockMessages = [
_MockMessage(
'Purchase Successful',
'You have successfully purchased Starbucks \$25 Gift Card for \$21.25',
'14:32',
MessageType.transaction,
false,
),
_MockMessage(
'Coupon Expiring Soon',
'Your Target \$30 Voucher will expire in 3 days',
'10:15',
MessageType.expiry,
false,
),
_MockMessage(
'Price Alert',
'Amazon \$100 Voucher price dropped to \$82, below your alert price',
'Yesterday',
MessageType.price,
true,
),
_MockMessage(
'Sale Completed',
'Your listed Nike \$80 Voucher has been sold for \$68.00',
'02/07',
MessageType.transaction,
true,
),
_MockMessage(
'Redeem Successful',
'Walmart \$50 Voucher redeemed at store',
'02/06',
MessageType.transaction,
true,
),
];