Files
wushu/lib/views/profile/per_card.dart
Developer d6ac0ed1e4 重构
2026-03-31 05:42:47 +08:00

653 lines
22 KiB
Dart

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<String, dynamic> userData;
final bool? isExpanded;
final ValueChanged<bool>? onExpandChanged;
final int currentPage;
final ValueChanged<int>? onPageChanged;
const PersonalCard({
super.key,
required this.userData,
this.isExpanded,
this.onExpandChanged,
this.currentPage = 1,
this.onPageChanged,
});
@override
State<PersonalCard> createState() => PersonalCardState();
}
class PersonalCardState extends State<PersonalCard> {
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<void> _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<void> refreshData() async {
await _loadTotalStats();
}
Future<void> _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<void> _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<String> _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,
),
),
],
),
),
);
}
}