From e2872b13fb0a3dcaf125cf125247b8c18f4315e1 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 24 Dec 2025 03:00:52 -0800 Subject: [PATCH] =?UTF-8?q?revert(mobile):=20=E5=9B=9E=E6=BB=9A=E7=81=AB?= =?UTF-8?q?=E6=9F=B4=E4=BA=BA=E7=BB=84=E4=BB=B6=E5=88=B0=E7=A8=B3=E5=AE=9A?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../widgets/stickman_race_widget.dart | 249 +++++++++--------- 1 file changed, 125 insertions(+), 124 deletions(-) diff --git a/frontend/mobile-app/lib/features/authorization/presentation/widgets/stickman_race_widget.dart b/frontend/mobile-app/lib/features/authorization/presentation/widgets/stickman_race_widget.dart index 5c292330..e3b1d424 100644 --- a/frontend/mobile-app/lib/features/authorization/presentation/widgets/stickman_race_widget.dart +++ b/frontend/mobile-app/lib/features/authorization/presentation/widgets/stickman_race_widget.dart @@ -8,7 +8,7 @@ class StickmanRankingData { final String nickname; final String? avatarUrl; final int completedCount; // 完成数量 - final int targetCount; // 目标数量 (省5万/市1万) + final int targetCount; // 目标数量 (省: 50000, 市: 10000) final double monthlyEarnings; // 本月可结算收益 final bool isCurrentUser; // 是否是当前用户 final String? accountSequence; // 账户序列号(用于查看详情) @@ -25,10 +25,7 @@ class StickmanRankingData { }); /// 完成进度 (0.0 - 1.0) - /// 直接用 completedCount / targetCount 计算,达到或超过目标就是100% - double get progress => targetCount > 0 - ? (completedCount / targetCount).clamp(0.0, 1.0) - : 0.0; + double get progress => (completedCount / targetCount).clamp(0.0, 1.0); } /// 授权类型 @@ -184,26 +181,21 @@ class _StickmanRaceWidgetState extends State return SizedBox( height: raceTrackHeight, - child: LayoutBuilder( - builder: (context, constraints) { - final containerWidth = constraints.maxWidth; - - // 收集所有 Positioned widgets - final List children = [ - // 背景赛道线 - Positioned.fill( - child: CustomPaint( - painter: _TrackPainter( - trackCount: sortedRankings.length, - ), + child: Stack( + children: [ + // 背景赛道线 + Positioned.fill( + child: CustomPaint( + painter: _TrackPainter( + trackCount: sortedRankings.length, ), ), - ]; + ), - // 添加每个跑道的终点红旗 - for (int index = 0; index < trackCount; index++) { + // 每个跑道的终点红旗 + ...List.generate(trackCount, (index) { final verticalPosition = index * trackHeight + 10 + trackHeight / 2 - 16; - children.add(Positioned( + return Positioned( right: 8, top: verticalPosition, child: const Icon( @@ -211,125 +203,134 @@ class _StickmanRaceWidgetState extends State color: Colors.red, size: 24, ), - )); - } + ); + }), - // 添加火柴人们(昵称和火柴人分开添加) - for (int index = 0; index < sortedRankings.length; index++) { - final data = sortedRankings[index]; - final widgets = _buildStickmanWidgets(data, index, sortedRankings.length, raceTrackHeight, containerWidth); - children.addAll(widgets); - } - - return AnimatedBuilder( - animation: _bounceController, - builder: (context, child) { - return Stack( - clipBehavior: Clip.none, - children: children, - ); - }, - ); - }, + // 火柴人们 + ...sortedRankings.asMap().entries.map((entry) { + final index = entry.key; + final data = entry.value; + return _buildStickman(data, index, sortedRankings.length, raceTrackHeight); + }), + ], ), ); } - /// 构建单个火柴人的所有 Positioned widgets - List _buildStickmanWidgets(StickmanRankingData data, int rank, int total, double raceTrackHeight, double containerWidth) { - // 进度 (0.0 - 1.0) - final progress = data.progress; + /// 构建单个火柴人 + Widget _buildStickman(StickmanRankingData data, int rank, int total, double raceTrackHeight) { + // 计算水平位置 (根据进度) + final horizontalProgress = data.progress; // 计算垂直位置 (不同排名在不同跑道) + // 预留顶部和底部空间 final usableHeight = raceTrackHeight - 40; final trackHeight = usableHeight / total; final verticalPosition = rank * trackHeight + 10; - // 简单计算: - // 起点 = 70 (昵称区域右边) - // 终点 = 红旗左边位置 - 火柴人宽度 = (containerWidth - 32) - 50 = containerWidth - 82 - const double startX = 70.0; - final double endX = containerWidth - 82.0; - final double stickmanLeft = startX + (endX - startX) * progress; + return AnimatedBuilder( + animation: _bounceController, + builder: (context, child) { + // 添加上下弹跳效果 + final bounce = _bounceController.value * 3; - final bounce = _bounceController.value * 3; + // 计算可用宽度:屏幕宽度 - 页面padding(32) - 容器padding(32) - 红旗区域(40) - 火柴人宽度一半(30) + // 让火柴人在100%时正好到达红旗位置 + final screenWidth = MediaQuery.of(context).size.width; + final containerWidth = screenWidth - 32 - 32; // 页面padding + 容器padding + final stickmanHalfWidth = 30.0; // 火柴人宽度的一半,用于居中对齐 + final flagAreaWidth = 40.0; // 红旗区域宽度(红旗在right:8位置,图标24+边距) + final nicknameAreaWidth = 65.0; // 昵称区域宽度 + final availableWidth = containerWidth - flagAreaWidth - stickmanHalfWidth - nicknameAreaWidth; + final leftPosition = nicknameAreaWidth + availableWidth * horizontalProgress; - return [ - // 昵称标签 - 固定在左边 - Positioned( - left: 0, - top: verticalPosition + 15 - bounce, - child: SizedBox( - width: 65, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), - decoration: BoxDecoration( - color: data.isCurrentUser - ? const Color(0xFFD4AF37).withValues(alpha: 0.2) - : Colors.white.withValues(alpha: 0.8), - borderRadius: BorderRadius.circular(8), - border: data.isCurrentUser - ? Border.all(color: const Color(0xFFD4AF37), width: 1) - : null, - ), - child: Text( - data.nickname, - style: TextStyle( - fontSize: 9, - fontFamily: 'Inter', - fontWeight: data.isCurrentUser ? FontWeight.w600 : FontWeight.w400, - color: data.isCurrentUser - ? const Color(0xFFD4AF37) - : const Color(0xFF5D4037), - ), - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.center, - ), - ), - ), - ), - // 火柴人和数量标签 - Positioned( - left: stickmanLeft, - top: verticalPosition - bounce, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // 完成数量标签 - Container( - constraints: const BoxConstraints(maxWidth: 60), - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: data.isCurrentUser - ? const Color(0xFFD4AF37) - : const Color(0xFF8B5A2B), - borderRadius: BorderRadius.circular(8), - ), - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - '${_formatNumber(data.completedCount)}棵', - style: const TextStyle( - fontSize: 9, - fontFamily: 'Inter', - fontWeight: FontWeight.w600, - color: Colors.white, + return Positioned( + left: 0, + top: verticalPosition - bounce, + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // 昵称标签 - 固定在左边 + SizedBox( + width: nicknameAreaWidth, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), + decoration: BoxDecoration( + color: data.isCurrentUser + ? const Color(0xFFD4AF37).withValues(alpha: 0.2) + : Colors.white.withValues(alpha: 0.8), + borderRadius: BorderRadius.circular(4), + border: data.isCurrentUser + ? Border.all(color: const Color(0xFFD4AF37), width: 1) + : null, + ), + child: Text( + data.nickname, + style: TextStyle( + fontSize: 9, + fontFamily: 'Inter', + fontWeight: + data.isCurrentUser ? FontWeight.w600 : FontWeight.w400, + color: data.isCurrentUser + ? const Color(0xFFD4AF37) + : const Color(0xFF5D4037), + ), + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, ), ), ), - ), - const SizedBox(height: 2), - // 火柴人动画 - RunningStickman( - size: 36, - color: data.isCurrentUser - ? const Color(0xFFD4AF37) - : const Color(0xFF5D4037), - ), - ], - ), - ), - ]; + // 火柴人和数量标签 + SizedBox( + width: leftPosition - nicknameAreaWidth + stickmanHalfWidth * 2, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 完成数量标签 + Container( + constraints: const BoxConstraints(maxWidth: 60), + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: data.isCurrentUser + ? const Color(0xFFD4AF37) + : const Color(0xFF8B5A2B), + borderRadius: BorderRadius.circular(8), + ), + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + '${_formatNumber(data.completedCount)}棵', + style: const TextStyle( + fontSize: 9, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + ), + ), + const SizedBox(height: 2), + // 火柴人动画 + RunningStickman( + size: 36, + color: data.isCurrentUser + ? const Color(0xFFD4AF37) + : const Color(0xFF5D4037), + ), + ], + ), + ], + ), + ), + ], + ), + ); + }, + ); } /// 构建进度条