import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'dart:math' as math; import '../view_model/song_view_model.dart'; class SongRecommendationView extends StatefulWidget { const SongRecommendationView({super.key}); @override State createState() => _SongRecommendationViewState(); } class _SongRecommendationViewState extends State { final SongViewModel songVM = Get.put(SongViewModel()); bool isLoading = false; @override void initState() { super.initState(); _loadRecommendedSongs(); } Future _loadRecommendedSongs() async { setState(() { isLoading = true; }); await Future.delayed(const Duration(seconds: 1)); setState(() { isLoading = false; }); } double _getCircleSize(double relevance) { return 100.0; } Map _getCirclePosition(int index, int totalItems, Size screenSize) { final radius = math.min(screenSize.width, screenSize.height) * 0.28; final angle = (index * 2 * math.pi / totalItems) - math.pi / 2; return { 'left': radius * math.cos(angle), 'top': radius * math.sin(angle), }; } @override Widget build(BuildContext context) { final screenSize = MediaQuery.of(context).size; final safeAreaPadding = MediaQuery.of(context).padding; return Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage("assets/img/app_bg.png"), fit: BoxFit.cover, ), ), child: Scaffold( backgroundColor: Colors.transparent, body: SafeArea( child: Stack( children: [ Positioned( top: 20, left: 0, right: 0, child: Center( child: Text( '知音推荐', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black.withOpacity(0.8), ), ), ), ), Center( child: Stack( alignment: Alignment.center, children: [ if (!isLoading) Obx(() => Stack( children: songVM.recommendedSongs.asMap().entries.map((entry) { final index = entry.key; final song = entry.value; final relevance = (song['relevance'] as num?)?.toDouble() ?? 0.5; final size = _getCircleSize(relevance); final position = _getCirclePosition( index, songVM.recommendedSongs.length, screenSize, ); return Positioned( left: screenSize.width / 2 + position['left']! - size / 2, top: screenSize.height / 2 + position['top']! - size / 2 - safeAreaPadding.top, child: Container( width: size, height: size, decoration: BoxDecoration( color: Colors.white.withOpacity(0.9), borderRadius: BorderRadius.circular(size / 2), boxShadow: [ BoxShadow( color: Colors.blue.withOpacity(0.1), blurRadius: 15, spreadRadius: 5, ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '${(relevance * 100).toInt()}%', style: TextStyle( color: Colors.blue[300], fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( (song['title'] as String?) ?? '', textAlign: TextAlign.center, style: const TextStyle( color: Colors.black87, fontWeight: FontWeight.bold, fontSize: 14, ), ), const SizedBox(height: 2), Text( (song['artist'] as String?) ?? '', textAlign: TextAlign.center, style: TextStyle( color: Colors.grey[600], fontSize: 12, ), ), ], ), ), ); }).toList(), )), Container( width: 80, height: 80, decoration: BoxDecoration( color: Colors.blue, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.blue.withOpacity(0.3), blurRadius: 15, spreadRadius: 5, ), ], ), child: IconButton( icon: Icon( isLoading ? Icons.hourglass_empty : Icons.refresh, color: Colors.white, size: 30, ), onPressed: () { if (!isLoading) { _loadRecommendedSongs(); } }, ), ), ], ), ), ], ), ), ), ); } }