import 'package:flutter/material.dart'; import 'dart:ui'; import 'dart:math'; import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../constants/app_constants.dart'; import '../../services/network_listener_service.dart'; import 'guide/tongji.dart'; /// 时间: 2026-03-25 /// 功能: 个人信息卡片组件 /// 介绍: 可收起/张开的个人信息卡片,支持下拉张开,上滑收起 /// 最新变化: 优化动画效果,添加毛玻璃背景,修复布局问题 class PersonalCard extends StatefulWidget { final Map userData; final bool? isExpanded; final ValueChanged? onExpandChanged; final int currentPage; final ValueChanged? onPageChanged; const PersonalCard({ super.key, required this.userData, this.isExpanded, this.onExpandChanged, this.currentPage = 1, this.onPageChanged, }); @override State createState() => PersonalCardState(); } class PersonalCardState extends State { late bool _isExpanded; late String _currentTip; bool _isOnline = true; // 累计数据 int _totalViews = 0; int _totalLikes = 0; int _totalQuestions = 0; @override void initState() { super.initState(); _isExpanded = widget.isExpanded ?? false; _currentTip = _getRandomTip(); _loadOnlineStatus(); _loadTotalStats(); } Future _loadTotalStats() async { final views = await StatisticsManager().getTotalViews(); final likes = await StatisticsManager().getTotalLikes(); final questions = await StatisticsManager().getTotalQuestions(); setState(() { _totalViews = views; _totalLikes = likes; _totalQuestions = questions; }); } // 刷新数据(公共方法,供外部调用) Future refreshData() async { await _loadTotalStats(); } Future _loadOnlineStatus() async { final prefs = await SharedPreferences.getInstance(); var isOnline = prefs.getBool('personal_card_online') ?? true; // 检查网络连接状态 try { final networkStatus = NetworkListenerService().currentStatus; // 如果网络处于错误状态,自动调整为离线状态 if (networkStatus == NetworkStatus.error) { isOnline = false; await prefs.setBool('personal_card_online', false); } } catch (e) { // 网络检查失败时保持原状态 } setState(() { _isOnline = isOnline; }); } Future _toggleOnlineStatus() async { // 如果当前是离线状态,要切换到在线状态,需要先检测网络 if (!_isOnline) { // 检测网络状态 try { final networkStatus = NetworkListenerService().currentStatus; if (networkStatus == NetworkStatus.error) { // 无网络,弹窗阻止用户打开 if (mounted) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('网络不可用'), content: const Text('当前无网络连接,无法切换到在线状态。请检查网络设置后重试。'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('知道了'), ), ], ); }, ); } return; } } catch (e) { // 网络检测失败,阻止切换 if (mounted) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('网络检测失败'), content: const Text('无法检测网络状态,请稍后重试。'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('知道了'), ), ], ); }, ); } return; } } setState(() { _isOnline = !_isOnline; }); final prefs = await SharedPreferences.getInstance(); await prefs.setBool('personal_card_online', _isOnline); // 显示气泡消息 ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(_isOnline ? '已切换到在线状态' : '已切换到离线状态'), duration: const Duration(seconds: 2), backgroundColor: _isOnline ? AppConstants.successColor : Colors.red, ), ); } @override void didUpdateWidget(covariant PersonalCard oldWidget) { super.didUpdateWidget(oldWidget); final newIsExpanded = widget.isExpanded ?? false; if (oldWidget.isExpanded != newIsExpanded) { setState(() { _isExpanded = newIsExpanded; }); } } void _toggleExpand() { setState(() { _isExpanded = !_isExpanded; widget.onExpandChanged?.call(_isExpanded); }); } final List _tips = [ "🐴年大🍊", "🌙安", "🧧到福到", "🍎🍎安安", "💪健康", "🐟🐟有余", "🍊🍈吉利", "🐲🐴精神", "💰满钵满", "😴好梦", "🐴年好☁️,大🍊大🍈,心想事🍊,万事🍅意!", "🌙了,早点🛏️,明天再战,加油💪!", "今天吃了🍎,平平安安;吃了🍊,大吉大利;喝了🥛,白白胖胖👶!", "祝你🐴到成功,💰源滚滚,步步高升📈,生活甜如蜜🍯!", "☀️记得吃饭🍳,元气满满一整天⭐!", "工作辛苦了,喝杯☕,听首歌🎵 放松一下🧘,烦恼全消☁️!", "愿你永远年轻🎉,早日上岸🤔!", "加油💪,认真📖,实现✅!", "旅途愉快✈️,注意安全🛡️,多拍美照📷,一路顺风💨!", "每天笑口常开😊,烦恼抛到九霄云外☁️,好运自然来🍀!", "今晚月色真美🌙,一起散步🚶,吹吹晚风💨,享受宁静时光。", "勇敢🐮🐮,不怕困难!", "天气转凉,注意保暖🧣,多喝热水💧,别感冒🤧!", "有空了? 约上朋友👬,吃顿火锅🍲,聊聊近况,快乐加倍🎉!", "新的一年,愿你拥有🐲🐴精神,事业如🐲头腾飞,家庭和睦🏠,幸福美满❤️!", ]; String _getRandomTip() { final random = Random(); return _tips[random.nextInt(_tips.length)]; } void _showRandomTip() { setState(() { _currentTip = _getRandomTip(); }); } @override Widget build(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.translucent, child: Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [ AppConstants.primaryColor.withValues(alpha: 0.95), AppConstants.primaryColor.withValues(alpha: 0.9), AppConstants.primaryColor.withValues(alpha: 0.85), ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: AnimatedContainer( duration: const Duration(milliseconds: 400), curve: Curves.easeInOut, padding: _isExpanded ? const EdgeInsets.all(16) : const EdgeInsets.symmetric(horizontal: 20, vertical: 12), height: _isExpanded ? null : 72, child: Stack( children: [ // 毛玻璃效果层(在文字按钮下层,背景色上层) BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container(color: Colors.transparent), ), // 内容层(在毛玻璃效果上层) _isExpanded ? _buildExpandedContent() : _buildCollapsedContent(), ], ), ), ), ); } Widget _buildCollapsedContent() { return Row( children: [ // 左侧区域:可点击展开/收起 Expanded( child: GestureDetector( onTap: _toggleExpand, child: Row( children: [ // 头像在左边 AnimatedContainer( duration: const Duration(milliseconds: 400), curve: Curves.easeInOut, child: Stack( children: [ CircleAvatar( radius: 24, backgroundColor: Colors.white, child: Text( widget.userData['avatar'] ?? '👤', style: const TextStyle(fontSize: 18), ), ), Positioned( bottom: 0, right: 0, child: Container( width: 16, height: 16, decoration: BoxDecoration( color: _isOnline ? AppConstants.successColor : Colors.red, border: Border.all(color: Colors.white, width: 2), borderRadius: BorderRadius.circular(8), ), child: Icon( _isOnline ? Icons.check : Icons.close, color: Colors.white, size: 10, ), ), ), ], ), ), const SizedBox(width: 12), // 名称和签名 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Text( widget.userData['nickname'] ?? '诗词爱好者', style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( widget.userData['signature'] ?? '人生如诗,岁月如歌', style: const TextStyle( color: Colors.white70, fontSize: 12, ), overflow: TextOverflow.ellipsis, ), ], ), ), ], ), ), ), const SizedBox(width: 12), // 右侧区域:标签栏,不可触发展开/收起 GestureDetector( behavior: HitTestBehavior.opaque, onTap: () {}, child: Row( children: [ _buildTabLabel(0, '卡片'), _buildTabLabel(1, '设置'), _buildTabLabel(2, '更多'), ], ), ), ], ); } Widget _buildExpandedContent() { return Column( mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 昵称和等级 Row( children: [ Text( widget.userData['nickname'] ?? '诗词爱好者', style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(width: 6), Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2, ), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(10), ), child: Text( widget.userData['level'] ?? 'Lv.1', style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.w500, ), ), ), ], ), const SizedBox(height: 4), // 个性签名 Text( widget.userData['signature'] ?? '人生如诗,岁月如歌', style: const TextStyle( color: Colors.white70, fontSize: 12, height: 1.2, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 10), // 累计统计卡片 Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 10, ), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Text( '累计', style: const TextStyle( color: Colors.white70, fontSize: 11, ), ), const SizedBox(height: 8), Row( mainAxisSize: MainAxisSize.min, children: [ _buildStatItem('浏览', _totalViews), const SizedBox(width: 16), _buildStatItem('点赞', _totalLikes), const SizedBox(width: 16), _buildStatItem('答题', _totalQuestions), ], ), ], ), ), ], ), ), const SizedBox(width: 10), // 头像在右边 AnimatedContainer( duration: const Duration(milliseconds: 400), curve: Curves.easeInOut, child: Stack( children: [ CircleAvatar( radius: 36, backgroundColor: Colors.white, child: Text( widget.userData['avatar'] ?? '👤', style: const TextStyle(fontSize: 20), ), ), Positioned( bottom: 0, right: 0, child: Container( width: 18, height: 18, decoration: BoxDecoration( color: _isOnline ? AppConstants.successColor : Colors.red, border: Border.all(color: Colors.white, width: 2), borderRadius: BorderRadius.circular(9), ), child: Icon( _isOnline ? Icons.check : Icons.close, color: Colors.white, size: 10, ), ), ), ], ), ), ], ), const SizedBox(height: 8), // 操作按钮 - 文本祝福语 Row( children: [ Expanded( child: GestureDetector( onTap: () { HapticFeedback.lightImpact(); _showRandomTip(); }, child: Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( '💡:$_currentTip', style: const TextStyle( color: AppConstants.primaryColor, fontSize: 14, fontWeight: FontWeight.w600, ), textAlign: TextAlign.left, maxLines: 3, ), ), const SizedBox(width: 12), // 在线状态开关 Switch( value: _isOnline, onChanged: (value) { _toggleOnlineStatus(); }, activeColor: AppConstants.primaryColor, inactiveTrackColor: Colors.grey.shade300, inactiveThumbColor: Colors.grey, ), ], ), ), ), ), ], ), // 标签栏 GestureDetector( behavior: HitTestBehavior.opaque, onTap: () {}, child: Container( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ // 页面指示器 Expanded( child: Container( height: 3, margin: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.3), borderRadius: BorderRadius.circular(2), ), child: Stack( children: [ // 背景条 Container( height: 3, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.3), borderRadius: BorderRadius.circular(2), ), ), // 滑动指示器 AnimatedContainer( duration: const Duration(milliseconds: 300), width: MediaQuery.of(context).size.width / 3 - 40, height: 3, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(2), ), margin: EdgeInsets.only( left: (MediaQuery.of(context).size.width / 3 - 40) * widget.currentPage, ), ), ], ), ), ), // 页面标签 _buildTabLabel(0, '卡片'), _buildTabLabel(1, '设置'), _buildTabLabel(2, '更多'), ], ), ), ), ], ); } Widget _buildStatItem(String label, int value) { return Column( children: [ Text( value.toString(), style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( label, style: const TextStyle(color: Colors.white70, fontSize: 12), ), ], ); } Widget _buildTabLabel(int index, String label) { // === 单个页面标签:可点击切换到对应页面 === final isSelected = widget.currentPage == index; return GestureDetector( onTap: () { widget.onPageChanged?.call(index); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( index == 0 ? Icons.credit_card : index == 1 ? Icons.settings : Icons.more_horiz, color: isSelected ? Colors.white : Colors.white70, size: 16, ), const SizedBox(height: 2), Text( label, style: TextStyle( color: isSelected ? Colors.white : Colors.white70, fontSize: 10, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ], ), ), ); } }