💄 Redesign leveling card

This commit is contained in:
2025-09-24 14:29:50 +08:00
parent 83e40cd860
commit bb9adb963a
4 changed files with 162 additions and 40 deletions

View File

@@ -336,6 +336,18 @@
"levelingProgress": "Leveling Progress",
"levelingProgressExperience": "{} EXP",
"levelingProgressLevel": "Level {}",
"levelingStage1": "Novice",
"levelingStage2": "Apprentice",
"levelingStage3": "Journeyman",
"levelingStage4": "Adept",
"levelingStage5": "Expert",
"levelingStage6": "Master",
"levelingStage7": "Grandmaster",
"levelingStage8": "Legend",
"levelingStage9": "Myth",
"levelingStage10": "Immortal",
"levelingStage11": "Divine",
"levelingStage12": "Transcendent",
"fileUploadingProgress": "Uploading file #{}: {}%",
"removeChatMember": "Remove Chat Room Member",
"removeChatMemberHint": "Are you sure to remove this member from the room?",

View File

@@ -283,6 +283,18 @@
"levelingProgress": "等级进度",
"levelingProgressExperience": "{} 经验值",
"levelingProgressLevel": "等级 {}",
"levelingStage1": "新手",
"levelingStage2": "学徒",
"levelingStage3": "熟练工",
"levelingStage4": "行家",
"levelingStage5": "专家",
"levelingStage6": "大师",
"levelingStage7": "宗师",
"levelingStage8": "传奇",
"levelingStage9": "神话",
"levelingStage10": "不朽",
"levelingStage11": "神圣",
"levelingStage12": "超凡",
"fileUploadingProgress": "正在上传文件 #{}: {}%",
"removeChatMember": "移除聊天室成员",
"removeChatMemberHint": "确定要将此成员从聊天室中移除吗?",

View File

@@ -141,12 +141,11 @@ class AccountScreen extends HookConsumerWidget {
],
),
).padding(horizontal: 8),
GestureDetector(
child: LevelingProgressCard(
level: user.value!.profile.level,
experience: user.value!.profile.experience,
progress: user.value!.profile.levelingProgress,
),
LevelingProgressCard(
isCompact: true,
level: user.value!.profile.level,
experience: user.value!.profile.experience,
progress: user.value!.profile.levelingProgress,
onTap: () {
context.pushNamed('leveling');
},

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:styled_widget/styled_widget.dart';
@@ -8,52 +7,152 @@ class LevelingProgressCard extends StatelessWidget {
final int level;
final int experience;
final double progress;
final VoidCallback? onTap;
final bool isCompact;
const LevelingProgressCard({
super.key,
required this.level,
required this.experience,
required this.progress,
this.onTap,
this.isCompact = false,
});
@override
Widget build(BuildContext context) {
return Card(
// Calculate level stage (1-12, each stage covers 10 levels)
int stage = ((level - 1) ~/ 10) + 1;
stage = stage.clamp(1, 12); // Ensure stage is within 1-12
// Define colors for each stage
const List<Color> stageColors = [
Colors.green,
Colors.blue,
Colors.teal,
Colors.cyan,
Colors.indigo,
Colors.lime,
Colors.yellow,
Colors.amber,
Colors.orange,
Colors.deepOrange,
Colors.pink,
Colors.red,
];
Color stageColor = stageColors[stage - 1];
// Compact mode adjustments
final double levelFontSize = isCompact ? 14 : 18;
final double stageFontSize = isCompact ? 13 : 14;
final double experienceFontSize = isCompact ? 12 : 14;
final double progressHeight = isCompact ? 6 : 10;
final double horizontalPadding = isCompact ? 16 : 20;
final double verticalPadding = isCompact ? 12 : 16;
final double gapSize = isCompact ? 4 : 8;
final double rowSpacing = 12;
final cardContent = Card(
margin: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.baseline,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
textBaseline: TextBaseline.alphabetic,
children: [
Text(
'levelingProgressLevel'.tr(args: [level.toString()]),
style: GoogleFonts.robotoMono(),
).fontSize(13).bold(),
Text(
'levelingProgressExperience'.tr(args: [experience.toString()]),
style: GoogleFonts.robotoMono(),
).fontSize(13),
],
),
const Gap(8),
Tooltip(
message: '${(progress).toStringAsFixed(1)}%',
child: LinearProgressIndicator(
minHeight: 4,
value: progress / 100,
stopIndicatorRadius: 0,
trackGap: 0,
color: Theme.of(context).colorScheme.primary,
backgroundColor:
Theme.of(context).colorScheme.surfaceContainerHigh,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [
stageColor.withOpacity(0.1),
Theme.of(context).colorScheme.surface,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
],
).padding(horizontal: 16, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
spacing: rowSpacing,
crossAxisAlignment: CrossAxisAlignment.baseline,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
textBaseline: TextBaseline.alphabetic,
children: [
Expanded(
child: Text(
'levelingProgressLevel'.tr(args: [level.toString()]),
style: TextStyle(
color: stageColor,
fontWeight: FontWeight.bold,
fontSize: levelFontSize,
),
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'levelingStage$stage'.tr(),
style: TextStyle(
color: stageColor.withOpacity(0.7),
fontWeight: FontWeight.w500,
fontSize: stageFontSize,
),
),
if (onTap != null) ...[
const Gap(4),
Icon(
Icons.arrow_forward_ios,
size: isCompact ? 10 : 12,
color: stageColor.withOpacity(0.7),
),
],
],
),
],
),
Gap(gapSize),
Row(
spacing: rowSpacing,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Tooltip(
message: '${progress.toStringAsFixed(1)}%',
child: LinearProgressIndicator(
minHeight: progressHeight,
value: progress,
borderRadius: BorderRadius.circular(32),
backgroundColor: Theme.of(
context,
).colorScheme.surfaceContainerLow.withOpacity(0.75),
color: stageColor,
stopIndicatorRadius: 0,
trackGap: 0,
),
),
),
Text(
'levelingProgressExperience'.tr(
args: [experience.toString()],
),
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.8),
fontSize: experienceFontSize,
),
),
],
),
],
).padding(horizontal: horizontalPadding, vertical: verticalPadding),
),
),
);
return cardContent;
}
}