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 createState() => _MessagePageState(); } class _MessagePageState extends State 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, ), ];