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 35ee1774..5c292330 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 @@ -188,47 +188,55 @@ class _StickmanRaceWidgetState extends State builder: (context, constraints) { final containerWidth = constraints.maxWidth; - return Stack( - clipBehavior: Clip.none, - children: [ - // 背景赛道线 - Positioned.fill( - child: CustomPaint( - painter: _TrackPainter( - trackCount: sortedRankings.length, - ), + // 收集所有 Positioned widgets + final List children = [ + // 背景赛道线 + Positioned.fill( + child: CustomPaint( + painter: _TrackPainter( + trackCount: sortedRankings.length, ), ), + ), + ]; - // 每个跑道的终点红旗 - ...List.generate(trackCount, (index) { - final verticalPosition = index * trackHeight + 10 + trackHeight / 2 - 16; - return Positioned( - right: 8, - top: verticalPosition, - child: const Icon( - Icons.flag, - color: Colors.red, - size: 24, - ), - ); - }), + // 添加每个跑道的终点红旗 + for (int index = 0; index < trackCount; index++) { + final verticalPosition = index * trackHeight + 10 + trackHeight / 2 - 16; + children.add(Positioned( + right: 8, + top: verticalPosition, + child: const Icon( + Icons.flag, + color: Colors.red, + size: 24, + ), + )); + } - // 火柴人们 - ...sortedRankings.asMap().entries.map((entry) { - final index = entry.key; - final data = entry.value; - return _buildStickman(data, index, sortedRankings.length, raceTrackHeight, containerWidth); - }), - ], + // 添加火柴人们(昵称和火柴人分开添加) + 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, + ); + }, ); }, ), ); } - /// 构建单个火柴人 - Widget _buildStickman(StickmanRankingData data, int rank, int total, double raceTrackHeight, double containerWidth) { + /// 构建单个火柴人的所有 Positioned widgets + List _buildStickmanWidgets(StickmanRankingData data, int rank, int total, double raceTrackHeight, double containerWidth) { // 进度 (0.0 - 1.0) final progress = data.progress; @@ -237,100 +245,91 @@ class _StickmanRaceWidgetState extends State final trackHeight = usableHeight / total; final verticalPosition = rank * trackHeight + 10; - // 火柴人列宽度约50(数量标签maxWidth:60,但实际更窄) - // 红旗: right=8, width=24, 所以红旗左边 = containerWidth - 32 - // 火柴人停在红旗左边时: stickmanLeft + 50 = containerWidth - 32 - // 所以 endX = containerWidth - 82 + // 简单计算: + // 起点 = 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; - return Stack( - clipBehavior: Clip.none, + 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: [ - // 昵称标签 - 固定在左边 - 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, + // 完成数量标签 + 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, ), ), ), ), - // 火柴人和数量标签 - 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, - ), - ), - ), - ), - const SizedBox(height: 2), - // 火柴人动画 - RunningStickman( - size: 36, - color: data.isCurrentUser - ? const Color(0xFFD4AF37) - : const Color(0xFF5D4037), - ), - ], - ), + const SizedBox(height: 2), + // 火柴人动画 + RunningStickman( + size: 36, + color: data.isCurrentUser + ? const Color(0xFFD4AF37) + : const Color(0xFF5D4037), ), ], - ); - }, - ); + ), + ), + ]; } /// 构建进度条