Auto-sync: 20260313_230001
This commit is contained in:
parent
518cb22ec4
commit
321810a6b4
10 changed files with 444 additions and 95 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
BIN
assets/audio/bgm/Music_Loop.mp3
Normal file
BIN
assets/audio/bgm/Music_Loop.mp3
Normal file
Binary file not shown.
BIN
assets/images/music_bg.jpg
Normal file
BIN
assets/images/music_bg.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
BIN
lib/.DS_Store
vendored
BIN
lib/.DS_Store
vendored
Binary file not shown.
|
|
@ -5,7 +5,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
enum AppThemeType { doodle, wood, cyberpunk, arcade, grimorio }
|
||||
enum AppThemeType { doodle, wood, cyberpunk, arcade, grimorio, music } // <-- Aggiunto 'music'
|
||||
|
||||
class ThemeColors {
|
||||
final Color background;
|
||||
|
|
@ -49,6 +49,15 @@ class AppColors {
|
|||
playerRed: Color(0xFFE91E63), playerBlue: Color(0xFF4FC3F7), text: Color(0xFFFFF3E0),
|
||||
);
|
||||
|
||||
// --- NUOVO TEMA MUSICA ---
|
||||
static const ThemeColors music = ThemeColors(
|
||||
background: Color(0xFF120B29), // Viola scuro (stile Synthwave)
|
||||
gridLine: Color(0xFF6A1B9A), // Viola elettrico
|
||||
playerRed: Color(0xFFFF2A6D), // Rosa acceso
|
||||
playerBlue: Color(0xFF05D5FF), // Ciano
|
||||
text: Color(0xFFE0E0E0),
|
||||
);
|
||||
|
||||
static ThemeColors getTheme(AppThemeType type) {
|
||||
switch (type) {
|
||||
case AppThemeType.doodle: return doodle;
|
||||
|
|
@ -56,6 +65,7 @@ class AppColors {
|
|||
case AppThemeType.cyberpunk: return cyberpunk;
|
||||
case AppThemeType.arcade: return arcade;
|
||||
case AppThemeType.grimorio: return grimorio;
|
||||
case AppThemeType.music: return music;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -68,6 +78,7 @@ class ThemeIcons {
|
|||
case AppThemeType.cyberpunk: return FontAwesomeIcons.microchip;
|
||||
case AppThemeType.arcade: return FontAwesomeIcons.coins;
|
||||
case AppThemeType.grimorio: return FontAwesomeIcons.crown;
|
||||
case AppThemeType.music: return FontAwesomeIcons.compactDisc; // CD/Vinile per i punti
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +89,7 @@ class ThemeIcons {
|
|||
case AppThemeType.cyberpunk: return FontAwesomeIcons.bug;
|
||||
case AppThemeType.arcade: return FontAwesomeIcons.ghost;
|
||||
case AppThemeType.grimorio: return FontAwesomeIcons.hatWizard;
|
||||
case AppThemeType.music: return FontAwesomeIcons.volumeXmark; // Muto/Errore per la bomba
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +100,7 @@ class ThemeIcons {
|
|||
case AppThemeType.cyberpunk: return FontAwesomeIcons.networkWired;
|
||||
case AppThemeType.arcade: return FontAwesomeIcons.shuffle;
|
||||
case AppThemeType.grimorio: return FontAwesomeIcons.hurricane;
|
||||
case AppThemeType.music: return FontAwesomeIcons.sliders; // Fader da DJ
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +111,7 @@ class ThemeIcons {
|
|||
case AppThemeType.cyberpunk: return FontAwesomeIcons.robot;
|
||||
case AppThemeType.arcade: return FontAwesomeIcons.gamepad;
|
||||
case AppThemeType.grimorio: return FontAwesomeIcons.masksTheater;
|
||||
case AppThemeType.music: return FontAwesomeIcons.headphones; // Cuffie per il Jolly
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,14 +122,17 @@ class ThemeIcons {
|
|||
case AppThemeType.cyberpunk: return FontAwesomeIcons.shieldHalved;
|
||||
case AppThemeType.arcade: return FontAwesomeIcons.powerOff;
|
||||
case AppThemeType.grimorio: return FontAwesomeIcons.meteor;
|
||||
case AppThemeType.music: return FontAwesomeIcons.pause; // Pausa per il blocco vuoto
|
||||
}
|
||||
}
|
||||
|
||||
static IconData ice(AppThemeType type) {
|
||||
if (type == AppThemeType.music) return FontAwesomeIcons.music; // Nota musicale ghiacciata
|
||||
return FontAwesomeIcons.snowflake;
|
||||
}
|
||||
|
||||
static IconData multiplier(AppThemeType type) {
|
||||
if (type == AppThemeType.music) return FontAwesomeIcons.forwardFast; // Fast Forward per il x2
|
||||
return FontAwesomeIcons.bolt;
|
||||
}
|
||||
}
|
||||
|
|
@ -60,6 +60,9 @@ class AudioService extends ChangeNotifier {
|
|||
case AppThemeType.grimorio:
|
||||
audioPath = 'audio/bgm/Grimorio_Astral.mp3';
|
||||
break;
|
||||
case AppThemeType.music:
|
||||
audioPath = 'audio/bgm/Music_Loop.mp3'; // <-- DEVI INSERIRE QUESTO FILE IN ASSETS
|
||||
break;
|
||||
}
|
||||
|
||||
if (audioPath.isNotEmpty) {
|
||||
|
|
@ -80,6 +83,7 @@ class AudioService extends ChangeNotifier {
|
|||
String file = '';
|
||||
switch (theme) {
|
||||
case AppThemeType.arcade:
|
||||
case AppThemeType.music: // Usiamo l'effetto arcade o cyber per la musica
|
||||
file = 'minimal_line.wav'; break;
|
||||
case AppThemeType.doodle:
|
||||
case AppThemeType.wood:
|
||||
|
|
@ -103,6 +107,7 @@ class AudioService extends ChangeNotifier {
|
|||
String file = '';
|
||||
switch (theme) {
|
||||
case AppThemeType.arcade:
|
||||
case AppThemeType.music:
|
||||
file = 'minimal_box.wav'; break;
|
||||
case AppThemeType.doodle:
|
||||
case AppThemeType.wood:
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ TextStyle _getTextStyle(AppThemeType themeType, TextStyle baseStyle) {
|
|||
));
|
||||
} else if (themeType == AppThemeType.grimorio) {
|
||||
return GoogleFonts.cinzelDecorative(textStyle: baseStyle.copyWith(fontWeight: FontWeight.bold));
|
||||
} else if (themeType == AppThemeType.music) {
|
||||
return GoogleFonts.audiowide(textStyle: baseStyle.copyWith(letterSpacing: 1.5));
|
||||
}
|
||||
return baseStyle;
|
||||
}
|
||||
|
|
@ -79,7 +81,7 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
|||
if (myName.isEmpty) myName = "TU";
|
||||
|
||||
String nameRed = controller.isOnline ? controller.onlineHostName.toUpperCase() : myName;
|
||||
String nameBlue = controller.isOnline ? controller.onlineGuestName.toUpperCase() : (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? "VERDE" : "BLU");
|
||||
String nameBlue = controller.isOnline ? controller.onlineGuestName.toUpperCase() : (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? "VERDE" : "BLU");
|
||||
if (controller.isVsCPU) nameBlue = "CPU";
|
||||
|
||||
String winnerText = ""; Color winnerColor = theme.text;
|
||||
|
|
@ -119,7 +121,7 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
|||
color: Colors.green.withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: Colors.greenAccent, width: 1.5),
|
||||
boxShadow: themeType == AppThemeType.cyberpunk ? [const BoxShadow(color: Colors.greenAccent, blurRadius: 10, spreadRadius: -5)] : [],
|
||||
boxShadow: (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) ? [const BoxShadow(color: Colors.greenAccent, blurRadius: 10, spreadRadius: -5)] : [],
|
||||
),
|
||||
child: Text("+ ${controller.lastMatchXP} XP", style: _getTextStyle(themeType, const TextStyle(color: Colors.greenAccent, fontWeight: FontWeight.w900, fontSize: 16, letterSpacing: 1.5))),
|
||||
),
|
||||
|
|
@ -194,7 +196,7 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
|||
titleText = "Nascondi il tuo Jolly!";
|
||||
subtitleText = "(Tocca qui per nascondere)";
|
||||
} else {
|
||||
String pName = gameController.jokerTurn == Player.red ? "ROSSO" : (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? "VERDE" : "BLU");
|
||||
String pName = gameController.jokerTurn == Player.red ? "ROSSO" : (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? "VERDE" : "BLU");
|
||||
titleText = "TURNO GIOCATORE $pName";
|
||||
subtitleText = "Passa il dispositivo.\nL'avversario NON deve guardare!\n\n(Tocca qui quando sei pronto)";
|
||||
}
|
||||
|
|
@ -204,7 +206,7 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
|||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(ThemeIcons.joker(themeType), color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.yellowAccent : theme.playerBlue, size: 50),
|
||||
Icon(ThemeIcons.joker(themeType), color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? Colors.yellowAccent : theme.playerBlue, size: 50),
|
||||
const SizedBox(height: 15),
|
||||
Text(
|
||||
titleText,
|
||||
|
|
@ -229,8 +231,8 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
|||
),
|
||||
);
|
||||
|
||||
if (themeType == AppThemeType.cyberpunk) {
|
||||
return Container(decoration: BoxDecoration(color: Colors.black.withOpacity(0.9), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.yellowAccent, width: 2), boxShadow: [const BoxShadow(color: Colors.yellowAccent, blurRadius: 15, spreadRadius: 0)]), child: content);
|
||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) {
|
||||
return Container(decoration: BoxDecoration(color: Colors.black.withOpacity(0.9), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.purpleAccent, width: 2), boxShadow: [BoxShadow(color: Colors.purpleAccent.withOpacity(0.6), blurRadius: 15, spreadRadius: 0)]), child: content);
|
||||
} else if (themeType == AppThemeType.doodle) {
|
||||
return Container(decoration: BoxDecoration(color: const Color(0xFFF9F9F9), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.black87, width: 3), boxShadow: const [BoxShadow(color: Colors.black26, offset: Offset(6, 6))]), child: content);
|
||||
} else if (themeType == AppThemeType.wood) {
|
||||
|
|
@ -289,8 +291,10 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
|||
String? bgImage;
|
||||
if (themeType == AppThemeType.wood) bgImage = 'assets/images/wood_bg.jpg';
|
||||
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg';
|
||||
if (themeType == AppThemeType.cyberpunk) bgImage = 'assets/images/cyber_bg.jpg';
|
||||
if (themeType == AppThemeType.music) bgImage = 'assets/images/music_bg.jpg';
|
||||
|
||||
Color indicatorColor = themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.white : Colors.black;
|
||||
Color indicatorColor = themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? Colors.white : Colors.black;
|
||||
|
||||
Widget emojiBar = const SizedBox();
|
||||
if (gameController.isOnline && !gameController.isGameOver) {
|
||||
|
|
@ -298,9 +302,9 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
|||
emojiBar = Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.black.withOpacity(0.6) : Colors.white.withOpacity(0.8),
|
||||
color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? Colors.black.withOpacity(0.6) : Colors.white.withOpacity(0.8),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
border: Border.all(color: themeType == AppThemeType.cyberpunk ? theme.playerBlue.withOpacity(0.3) : Colors.white24, width: 2),
|
||||
border: Border.all(color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music ? theme.playerBlue.withOpacity(0.3) : Colors.white24, width: 2),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
@ -382,10 +386,10 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
|||
Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.4), offset: const Offset(0, 4), blurRadius: 5)]),
|
||||
child: TextButton.icon(
|
||||
style: TextButton.styleFrom(backgroundColor: bgImage != null || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.black87 : theme.background, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20), side: BorderSide(color: Colors.white.withOpacity(0.1), width: 1))),
|
||||
icon: Icon(Icons.exit_to_app, color: bgImage != null || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.white : theme.text, size: 20),
|
||||
style: TextButton.styleFrom(backgroundColor: bgImage != null || themeType == AppThemeType.arcade ? Colors.black87 : theme.background, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20), side: BorderSide(color: Colors.white.withOpacity(0.1), width: 1))),
|
||||
icon: Icon(Icons.exit_to_app, color: bgImage != null || themeType == AppThemeType.arcade ? Colors.white : theme.text, size: 20),
|
||||
onPressed: () { gameController.disconnectOnlineGame(); Navigator.pop(context); },
|
||||
label: Text("ESCI", style: _getTextStyle(themeType, TextStyle(color: bgImage != null || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.white : theme.text, fontWeight: FontWeight.bold, fontSize: 12))),
|
||||
label: Text("ESCI", style: _getTextStyle(themeType, TextStyle(color: bgImage != null || themeType == AppThemeType.arcade ? Colors.white : theme.text, fontWeight: FontWeight.bold, fontSize: 12))),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -406,23 +410,25 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
|||
canPop: true,
|
||||
onPopInvoked: (didPop) { gameController.disconnectOnlineGame(); },
|
||||
child: Scaffold(
|
||||
// L'impostazione dello sfondo dipende dal tema
|
||||
backgroundColor: themeType == AppThemeType.aurora ? Colors.transparent : (bgImage != null ? Colors.transparent : theme.background),
|
||||
backgroundColor: bgImage != null ? Colors.transparent : theme.background,
|
||||
body: Container(
|
||||
// GRADIENTE AURORA IN BACKGROUND!
|
||||
decoration: themeType == AppThemeType.aurora
|
||||
? const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [Color(0xFF2A0845), Color(0xFFDD2476), Color(0xFFFF512F)],
|
||||
),
|
||||
)
|
||||
: (bgImage != null ? BoxDecoration(image: DecorationImage(image: AssetImage(bgImage), fit: BoxFit.cover, colorFilter: themeType == AppThemeType.doodle ? ColorFilter.mode(Colors.white.withOpacity(0.7), BlendMode.lighten) : null)) : null),
|
||||
decoration: bgImage != null ? BoxDecoration(image: DecorationImage(image: AssetImage(bgImage), fit: BoxFit.cover, colorFilter: themeType == AppThemeType.doodle ? ColorFilter.mode(Colors.white.withOpacity(0.7), BlendMode.lighten) : null)) : null,
|
||||
child: CustomPaint(
|
||||
// painter rimosso per supportare il nuovo sfondo a gradiente
|
||||
painter: themeType == AppThemeType.doodle ? FullScreenGridPainter(Colors.black.withOpacity(0.06)) : null,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (bgImage != null && (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music))
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter,
|
||||
colors: [Colors.black.withOpacity(0.3), Colors.black.withOpacity(0.8)]
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
if (gameController.isTimeMode && !gameController.isCPUThinking && !gameController.isGameOver && gameController.timeLeft > 0 && gameController.timeLeft <= 5 && !gameController.isSetupPhase)
|
||||
Positioned.fill(child: BlitzBackgroundEffect(timeLeft: gameController.timeLeft, color: theme.playerRed, themeType: themeType)),
|
||||
|
||||
|
|
@ -434,7 +440,7 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
|||
if (gameController.isSetupPhase && !_hideJokerMessage)
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora
|
||||
color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music
|
||||
? Colors.black.withOpacity(0.98)
|
||||
: theme.background.withOpacity(0.98),
|
||||
child: Center(
|
||||
|
|
@ -520,7 +526,7 @@ class _WinnerVFXOverlayState extends State<WinnerVFXOverlay> with SingleTickerPr
|
|||
}
|
||||
|
||||
void _initParticles(Size screenSize) {
|
||||
int particleCount = widget.themeType == AppThemeType.cyberpunk || widget.themeType == AppThemeType.aurora ? 150 : 100;
|
||||
int particleCount = widget.themeType == AppThemeType.cyberpunk || widget.themeType == AppThemeType.music ? 150 : 100;
|
||||
if (widget.themeType == AppThemeType.arcade) particleCount = 80;
|
||||
if (widget.themeType == AppThemeType.grimorio) particleCount = 120;
|
||||
|
||||
|
|
@ -530,7 +536,7 @@ class _WinnerVFXOverlayState extends State<WinnerVFXOverlay> with SingleTickerPr
|
|||
else if (widget.themeType == AppThemeType.wood) { palette = [Colors.orangeAccent, Colors.yellow, Colors.red, Colors.white]; }
|
||||
else if (widget.themeType == AppThemeType.arcade) { palette = [widget.winnerColor, Colors.white, Colors.greenAccent]; }
|
||||
else if (widget.themeType == AppThemeType.grimorio) { palette = [widget.winnerColor, Colors.deepPurpleAccent, Colors.white]; }
|
||||
else if (widget.themeType == AppThemeType.aurora) { palette = [widget.winnerColor, Colors.white, Colors.orangeAccent, Colors.pinkAccent]; }
|
||||
else if (widget.themeType == AppThemeType.music) { palette.add(Colors.pinkAccent); palette.add(Colors.cyanAccent); }
|
||||
|
||||
for (int i = 0; i < particleCount; i++) {
|
||||
double speed = _rand.nextDouble() * 20 + 5;
|
||||
|
|
@ -543,7 +549,7 @@ class _WinnerVFXOverlayState extends State<WinnerVFXOverlay> with SingleTickerPr
|
|||
setState(() {
|
||||
for (var p in _particles) {
|
||||
p.x += p.vx; p.y += p.vy;
|
||||
if (widget.themeType == AppThemeType.cyberpunk || widget.themeType == AppThemeType.aurora) { p.vy += 0.1; p.vx *= 0.98; p.vy *= 0.98; }
|
||||
if (widget.themeType == AppThemeType.cyberpunk || widget.themeType == AppThemeType.music) { p.vy += 0.1; p.vx *= 0.98; p.vy *= 0.98; }
|
||||
else if (widget.themeType == AppThemeType.wood) { p.vy -= 0.2; p.x += math.sin(p.y * 0.05) * 2; }
|
||||
else if (widget.themeType == AppThemeType.arcade) { p.vy += 0.3; p.spin = 0; p.angle = 0; }
|
||||
else if (widget.themeType == AppThemeType.grimorio) { p.vy -= 0.1; p.x += math.sin(p.y * 0.02) * 1.5; p.size *= 0.995; }
|
||||
|
|
@ -566,7 +572,7 @@ class _VFXPainter extends CustomPainter {
|
|||
for (var p in particles) {
|
||||
if (p.size < 0.5) continue;
|
||||
final paint = Paint()..color = p.color..style = PaintingStyle.fill;
|
||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.aurora) { paint.maskFilter = const MaskFilter.blur(BlurStyle.solid, 4.0); }
|
||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) { paint.maskFilter = const MaskFilter.blur(BlurStyle.solid, 4.0); }
|
||||
canvas.save(); canvas.translate(p.x, p.y); canvas.rotate(p.angle);
|
||||
|
||||
if (themeType == AppThemeType.doodle) {
|
||||
|
|
@ -603,7 +609,6 @@ class _BouncingEmojiState extends State<_BouncingEmoji> with SingleTickerProvide
|
|||
@override Widget build(BuildContext context) { return AnimatedBuilder(animation: _anim, builder: (ctx, child) => Transform.translate(offset: Offset(0, _anim.value), child: Container(padding: const EdgeInsets.all(8), decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle, boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 5)]), child: Text(widget.emoji, style: const TextStyle(fontSize: 32))))); }
|
||||
}
|
||||
|
||||
// Mantengo la definizione per evitare buchi, ma ora Aurora usa il LinearGradient!
|
||||
class FullScreenGridPainter extends CustomPainter {
|
||||
final Color gridColor; FullScreenGridPainter(this.gridColor);
|
||||
@override void paint(Canvas canvas, Size size) { final Paint paperGridPaint = Paint()..color = gridColor..strokeWidth = 1.0..style = PaintingStyle.stroke; double paperStep = 20.0; for (double i = 0; i <= size.width; i += paperStep) canvas.drawLine(Offset(i, 0), Offset(i, size.height), paperGridPaint); for (double i = 0; i <= size.height; i += paperStep) canvas.drawLine(Offset(0, i), Offset(size.width, i), paperGridPaint); }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
// Import separati e puliti
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import '../../logic/game_controller.dart';
|
||||
import '../../models/game_board.dart';
|
||||
import '../../core/theme_manager.dart';
|
||||
|
|
@ -12,6 +12,20 @@ import '../../services/audio_service.dart';
|
|||
import '../../core/app_colors.dart';
|
||||
import '../../services/storage_service.dart';
|
||||
|
||||
TextStyle _getTextStyle(AppThemeType themeType, TextStyle baseStyle) {
|
||||
if (themeType == AppThemeType.doodle) {
|
||||
return GoogleFonts.permanentMarker(textStyle: baseStyle);
|
||||
} else if (themeType == AppThemeType.arcade) {
|
||||
return GoogleFonts.pressStart2p(textStyle: baseStyle.copyWith(
|
||||
fontSize: baseStyle.fontSize != null ? baseStyle.fontSize! * 0.75 : null,
|
||||
letterSpacing: 0.5,
|
||||
));
|
||||
} else if (themeType == AppThemeType.grimorio) {
|
||||
return GoogleFonts.cinzelDecorative(textStyle: baseStyle.copyWith(fontWeight: FontWeight.bold));
|
||||
}
|
||||
return baseStyle;
|
||||
}
|
||||
|
||||
class ScoreBoard extends StatefulWidget {
|
||||
const ScoreBoard({super.key});
|
||||
|
||||
|
|
@ -50,7 +64,7 @@ class _ScoreBoardState extends State<ScoreBoard> {
|
|||
return Container(
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 20, left: 20, right: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.background.withOpacity(0.95),
|
||||
color: themeType == AppThemeType.doodle ? theme.background : theme.background.withOpacity(0.95),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.3),
|
||||
|
|
@ -66,20 +80,26 @@ class _ScoreBoardState extends State<ScoreBoard> {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_PlayerScore(color: theme.playerRed, score: redScore, isTurn: isRedTurn, textColor: theme.text, title: nameRed),
|
||||
_PlayerScore(color: theme.playerRed, score: redScore, isTurn: isRedTurn, textColor: theme.text, title: nameRed, themeType: themeType),
|
||||
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"TETRAQ",
|
||||
style: TextStyle(
|
||||
style: _getTextStyle(themeType, TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: theme.text,
|
||||
letterSpacing: 4,
|
||||
shadows: [Shadow(color: Colors.black.withOpacity(0.3), offset: const Offset(1, 2), blurRadius: 2)]
|
||||
)
|
||||
shadows: themeType == AppThemeType.doodle
|
||||
? [
|
||||
// EFFETTO RILIEVO (Luce in alto a sx, ombra in basso a dx)
|
||||
const Shadow(color: Colors.white, offset: Offset(-1.5, -1.5), blurRadius: 1),
|
||||
Shadow(color: Colors.black.withOpacity(0.25), offset: const Offset(1.5, 1.5), blurRadius: 2),
|
||||
]
|
||||
: [Shadow(color: Colors.black.withOpacity(0.3), offset: const Offset(1, 2), blurRadius: 2)]
|
||||
))
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(isMuted ? Icons.volume_off : Icons.volume_up, color: theme.text.withOpacity(0.7)),
|
||||
|
|
@ -92,7 +112,7 @@ class _ScoreBoardState extends State<ScoreBoard> {
|
|||
],
|
||||
),
|
||||
|
||||
_PlayerScore(color: theme.playerBlue, score: blueScore, isTurn: !isRedTurn, textColor: theme.text, title: nameBlue),
|
||||
_PlayerScore(color: theme.playerBlue, score: blueScore, isTurn: !isRedTurn, textColor: theme.text, title: nameBlue, themeType: themeType),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
@ -105,15 +125,16 @@ class _PlayerScore extends StatelessWidget {
|
|||
final bool isTurn;
|
||||
final Color textColor;
|
||||
final String title;
|
||||
final AppThemeType themeType;
|
||||
|
||||
const _PlayerScore({required this.color, required this.score, required this.isTurn, required this.textColor, required this.title});
|
||||
const _PlayerScore({required this.color, required this.score, required this.isTurn, required this.textColor, required this.title, required this.themeType});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(title, style: TextStyle(fontWeight: FontWeight.bold, color: isTurn ? color : textColor.withOpacity(0.5), fontSize: 12)),
|
||||
Text(title, style: _getTextStyle(themeType, TextStyle(fontWeight: FontWeight.bold, color: isTurn ? color : textColor.withOpacity(0.5), fontSize: 12))),
|
||||
const SizedBox(height: 5),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
|
|
@ -126,7 +147,7 @@ class _PlayerScore extends StatelessWidget {
|
|||
BoxShadow(color: color.withOpacity(0.5), offset: const Offset(0, 4), blurRadius: 6)
|
||||
] : [],
|
||||
),
|
||||
child: Text('$score', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: isTurn ? Colors.white : textColor.withOpacity(0.5))),
|
||||
child: Text('$score', style: _getTextStyle(themeType, TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: isTurn ? Colors.white : textColor.withOpacity(0.5)))),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import 'dart:math' as math;
|
|||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; // Aggiunto per le nuove icone musicali
|
||||
|
||||
import '../../logic/game_controller.dart';
|
||||
import '../../core/theme_manager.dart';
|
||||
|
|
@ -36,6 +37,8 @@ TextStyle _getTextStyle(AppThemeType themeType, TextStyle baseStyle) {
|
|||
));
|
||||
} else if (themeType == AppThemeType.grimorio) {
|
||||
return GoogleFonts.cinzelDecorative(textStyle: baseStyle.copyWith(fontWeight: FontWeight.bold));
|
||||
} else if (themeType == AppThemeType.music) {
|
||||
return GoogleFonts.audiowide(textStyle: baseStyle.copyWith(letterSpacing: 1.5));
|
||||
}
|
||||
return baseStyle;
|
||||
}
|
||||
|
|
@ -535,7 +538,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
),
|
||||
);
|
||||
|
||||
if (themeType == AppThemeType.cyberpunk) dialogContent = _AnimatedCyberBorder(child: dialogContent);
|
||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) dialogContent = _AnimatedCyberBorder(child: dialogContent);
|
||||
return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.all(20), child: dialogContent);
|
||||
},
|
||||
);
|
||||
|
|
@ -754,8 +757,8 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [theme.background.withOpacity(0.95), theme.background.withOpacity(0.8)]),
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
border: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? null : Border.all(color: Colors.white.withOpacity(0.15), width: 1.5),
|
||||
boxShadow: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? [] : [BoxShadow(color: Colors.black.withOpacity(0.5), blurRadius: 20, offset: const Offset(4, 10))],
|
||||
border: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? null : Border.all(color: Colors.white.withOpacity(0.15), width: 1.5),
|
||||
boxShadow: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? [] : [BoxShadow(color: Colors.black.withOpacity(0.5), blurRadius: 20, offset: const Offset(4, 10))],
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
|
|
@ -851,7 +854,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
),
|
||||
);
|
||||
|
||||
if (themeType == AppThemeType.cyberpunk) {
|
||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) {
|
||||
dialogContent = _AnimatedCyberBorder(child: dialogContent);
|
||||
}
|
||||
|
||||
|
|
@ -1053,7 +1056,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
),
|
||||
);
|
||||
|
||||
if (themeType == AppThemeType.cyberpunk) content = _AnimatedCyberBorder(child: content);
|
||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) content = _AnimatedCyberBorder(child: content);
|
||||
return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.all(20), child: content);
|
||||
}
|
||||
);
|
||||
|
|
@ -1134,8 +1137,8 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [theme.background.withOpacity(0.95), theme.background.withOpacity(0.8)]),
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
border: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? null : Border.all(color: Colors.white.withOpacity(0.15), width: 1.5),
|
||||
boxShadow: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? [] : [BoxShadow(color: Colors.black.withOpacity(0.5), blurRadius: 20, offset: const Offset(4, 10))],
|
||||
border: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? null : Border.all(color: Colors.white.withOpacity(0.15), width: 1.5),
|
||||
boxShadow: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? [] : [BoxShadow(color: Colors.black.withOpacity(0.5), blurRadius: 20, offset: const Offset(4, 10))],
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
|
|
@ -1182,7 +1185,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
),
|
||||
);
|
||||
|
||||
if (themeType == AppThemeType.cyberpunk) dialogContent = _AnimatedCyberBorder(child: dialogContent);
|
||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) dialogContent = _AnimatedCyberBorder(child: dialogContent);
|
||||
return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), child: dialogContent);
|
||||
},
|
||||
);
|
||||
|
|
@ -1192,7 +1195,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
if (themeType == AppThemeType.cyberpunk) {
|
||||
return _AnimatedCyberBorder(child: card);
|
||||
}
|
||||
return card;
|
||||
return card; // La card musicale ha già i propri effetti integrati
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -1208,8 +1211,8 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
|
||||
String? bgImage;
|
||||
if (themeType == AppThemeType.wood) bgImage = 'assets/images/wood_bg.jpg';
|
||||
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg';
|
||||
if (themeType == AppThemeType.cyberpunk) bgImage = 'assets/images/cyber_bg.jpg';
|
||||
if (themeType == AppThemeType.music) bgImage = 'assets/images/music_bg.jpg';
|
||||
|
||||
int wins = StorageService.instance.wins;
|
||||
int losses = StorageService.instance.losses;
|
||||
|
|
@ -1306,9 +1309,9 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.emoji_events, color: Colors.amber.shade600, size: 20), const SizedBox(width: 6),
|
||||
Icon(themeType == AppThemeType.music ? FontAwesomeIcons.microphone : Icons.emoji_events, color: Colors.amber.shade600, size: 16), const SizedBox(width: 6),
|
||||
Text("$wins", style: _getTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.w900))), const SizedBox(width: 12),
|
||||
Icon(Icons.sentiment_very_dissatisfied, color: theme.playerRed.withOpacity(0.8), size: 20), const SizedBox(width: 6),
|
||||
Icon(themeType == AppThemeType.music ? FontAwesomeIcons.compactDisc : Icons.sentiment_very_dissatisfied, color: theme.playerRed.withOpacity(0.8), size: 16), const SizedBox(width: 6),
|
||||
Text("$losses", style: _getTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.w900))),
|
||||
],
|
||||
),
|
||||
|
|
@ -1322,7 +1325,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
Center(
|
||||
child: Transform.rotate(
|
||||
angle: themeType == AppThemeType.doodle ? -0.04 : 0,
|
||||
// --- IL TRUCCO DELLO SVILUPPATORE PROTETTO ---
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_debugTapCount++;
|
||||
|
|
@ -1330,16 +1332,10 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
StorageService.instance.addXP(2000);
|
||||
setState(() {});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text("🛠 DEBUG MODE: +20 Livelli!", style: _getTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.bold))),
|
||||
backgroundColor: Colors.purpleAccent,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
|
||||
)
|
||||
SnackBar(content: Text("🛠 DEBUG MODE: +20 Livelli!", style: _getTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.bold))), backgroundColor: Colors.purpleAccent, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)))
|
||||
);
|
||||
} else if (_debugTapCount >= 7) {
|
||||
_debugTapCount = 0; // Resetta il contatore
|
||||
// APRE LA DASHBOARD SEGRETA!
|
||||
_debugTapCount = 0;
|
||||
Navigator.push(context, MaterialPageRoute(builder: (_) => const AdminScreen()));
|
||||
}
|
||||
},
|
||||
|
|
@ -1352,7 +1348,12 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
fontWeight: FontWeight.w900,
|
||||
color: themeType == AppThemeType.doodle ? inkColor : theme.text,
|
||||
letterSpacing: 10,
|
||||
shadows: themeType == AppThemeType.doodle || themeType == AppThemeType.arcade ? [] : [
|
||||
shadows: themeType == AppThemeType.doodle
|
||||
? [
|
||||
const Shadow(color: Colors.white, offset: Offset(-2.5, -2.5), blurRadius: 0),
|
||||
Shadow(color: Colors.black.withOpacity(0.25), offset: const Offset(2.5, 2.5), blurRadius: 1),
|
||||
]
|
||||
: themeType == AppThemeType.arcade || themeType == AppThemeType.music ? [] : [
|
||||
BoxShadow(color: Colors.black.withOpacity(0.6), offset: const Offset(3, 6), blurRadius: 8),
|
||||
BoxShadow(color: theme.playerBlue.withOpacity(0.4), offset: const Offset(0, 0), blurRadius: 20),
|
||||
]
|
||||
|
|
@ -1365,7 +1366,38 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
|
||||
const Spacer(),
|
||||
|
||||
// --- NUOVA LISTA BOTTONI ---
|
||||
// --- GESTIONE DEI MENU IN BASE AL TEMA ---
|
||||
if (themeType == AppThemeType.music) ...[
|
||||
_MusicCassetteCard(
|
||||
title: loc.onlineTitle, subtitle: loc.onlineSub, neonColor: Colors.blueAccent, angle: -0.04,
|
||||
leftIcon: FontAwesomeIcons.sliders, rightIcon: FontAwesomeIcons.globe, themeType: themeType,
|
||||
onTap: () { Navigator.push(context, MaterialPageRoute(builder: (_) => const LobbyScreen())); },
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_MusicCassetteCard(
|
||||
title: loc.cpuTitle, subtitle: loc.cpuSub, neonColor: Colors.purpleAccent, angle: 0.03,
|
||||
leftIcon: FontAwesomeIcons.desktop, rightIcon: FontAwesomeIcons.music, themeType: themeType,
|
||||
onTap: () => _showMatchSetupDialog(true),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_MusicCassetteCard(
|
||||
title: loc.localTitle, subtitle: loc.localSub, neonColor: Colors.deepPurpleAccent, angle: -0.02,
|
||||
leftIcon: FontAwesomeIcons.headphones, rightIcon: FontAwesomeIcons.headphones, themeType: themeType,
|
||||
onTap: () => _showMatchSetupDialog(false),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: _MusicKnobCard(title: loc.leaderboardTitle, icon: FontAwesomeIcons.compactDisc, iconColor: Colors.amber, themeType: themeType, onTap: _showLeaderboardDialog)),
|
||||
Expanded(child: _MusicKnobCard(title: loc.questsTitle, icon: FontAwesomeIcons.microphoneLines, themeType: themeType, onTap: _showDailyQuestsDialog)),
|
||||
Expanded(child: _MusicKnobCard(title: loc.themesTitle, icon: FontAwesomeIcons.palette, themeType: themeType, onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const SettingsScreen())))),
|
||||
Expanded(child: _MusicKnobCard(title: loc.tutorialTitle, icon: FontAwesomeIcons.bookOpen, themeType: themeType, onTap: _showTutorialDialog)),
|
||||
],
|
||||
),
|
||||
] else ...[
|
||||
// --- LISTA BOTTONI ORIGINALE ---
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
|
|
@ -1395,6 +1427,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
|
|
@ -1407,27 +1440,43 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
|||
);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: bgImage != null ? Colors.transparent : theme.background,
|
||||
backgroundColor: themeType == AppThemeType.doodle ? Colors.white : (bgImage != null ? Colors.transparent : theme.background),
|
||||
body: Stack(
|
||||
children: [
|
||||
Container(color: theme.background),
|
||||
Container(color: themeType == AppThemeType.doodle ? Colors.white : theme.background),
|
||||
if (themeType == AppThemeType.doodle)
|
||||
Positioned.fill(
|
||||
child: CustomPaint(
|
||||
painter: FullScreenGridPainter(Colors.blue.withOpacity(0.15)),
|
||||
),
|
||||
),
|
||||
if (bgImage != null)
|
||||
Positioned.fill(
|
||||
child: Image.asset(bgImage, fit: BoxFit.cover, alignment: Alignment.center),
|
||||
),
|
||||
if (bgImage != null)
|
||||
Positioned.fill(
|
||||
child: themeType == AppThemeType.cyberpunk
|
||||
child: (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music)
|
||||
? Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter,
|
||||
colors: [Colors.black.withOpacity(0.2), Colors.black.withOpacity(0.7)]
|
||||
colors: [Colors.black.withOpacity(0.4), Colors.black.withOpacity(0.8)]
|
||||
)
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
),
|
||||
|
||||
if (themeType == AppThemeType.music)
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
child: CustomPaint(
|
||||
painter: _AudioCablesPainter(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Positioned.fill(child: uiContent),
|
||||
],
|
||||
),
|
||||
|
|
@ -1460,6 +1509,249 @@ class _TutorialStep extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
// --- WIDGETS SPECIFICI TEMA MUSICA ---
|
||||
|
||||
class _MusicCassetteCard extends StatelessWidget {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final Color neonColor;
|
||||
final double angle;
|
||||
final IconData leftIcon;
|
||||
final IconData rightIcon;
|
||||
final VoidCallback onTap;
|
||||
final AppThemeType themeType;
|
||||
|
||||
const _MusicCassetteCard({required this.title, required this.subtitle, required this.neonColor, required this.angle, required this.leftIcon, required this.rightIcon, required this.onTap, required this.themeType});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Transform.rotate(
|
||||
angle: angle,
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 120,
|
||||
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 10),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF22222A), // Materic plastic
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.black87, width: 2),
|
||||
boxShadow: [
|
||||
BoxShadow(color: neonColor.withOpacity(0.5), blurRadius: 25, spreadRadius: 2),
|
||||
const BoxShadow(color: Colors.black54, offset: Offset(5, 10), blurRadius: 15),
|
||||
]
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// Cassette Label
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: neonColor.withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(color: neonColor.withOpacity(0.5), width: 1.5),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(padding: const EdgeInsets.symmetric(horizontal: 12), child: Icon(leftIcon, color: neonColor, size: 28)),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FittedBox(fit: BoxFit.scaleDown, child: Text(title, style: _getTextStyle(themeType, TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.w900, shadows: [Shadow(color: neonColor, blurRadius: 10)])))),
|
||||
const SizedBox(height: 2),
|
||||
FittedBox(fit: BoxFit.scaleDown, child: Text(subtitle, style: _getTextStyle(themeType, const TextStyle(color: Colors.white70, fontSize: 12, fontWeight: FontWeight.bold)))),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(padding: const EdgeInsets.symmetric(horizontal: 12), child: Icon(rightIcon, color: neonColor, size: 28)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// Spools window
|
||||
Container(
|
||||
height: 35,
|
||||
width: 180,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF0D0D12),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: Colors.white24, width: 1),
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Tape line connecting spools
|
||||
Container(height: 2, width: 120, color: const Color(0xFF333333)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
_buildSpool(),
|
||||
_buildSpool(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSpool() {
|
||||
return Container(
|
||||
width: 26, height: 26,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white70,
|
||||
border: Border.all(color: Colors.black87, width: 5)
|
||||
),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 6, height: 6,
|
||||
decoration: const BoxDecoration(shape: BoxShape.circle, color: Colors.black),
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MusicKnobCard extends StatelessWidget {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final VoidCallback onTap;
|
||||
final AppThemeType themeType;
|
||||
final Color? iconColor;
|
||||
|
||||
const _MusicKnobCard({required this.title, required this.icon, required this.onTap, required this.themeType, this.iconColor});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 65, height: 65,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: const Color(0xFF222222), // Base plastic
|
||||
border: Border.all(color: const Color(0xFF111111), width: 2),
|
||||
boxShadow: const [
|
||||
BoxShadow(color: Colors.black87, blurRadius: 10, offset: Offset(2, 6)),
|
||||
BoxShadow(color: Colors.white12, blurRadius: 2, offset: Offset(-1, -1)),
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(6.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: const SweepGradient(
|
||||
colors: [
|
||||
Color(0xFF555555), Color(0xFFAAAAAA), Color(0xFF555555),
|
||||
Color(0xFF222222), Color(0xFF555555)
|
||||
],
|
||||
),
|
||||
border: Border.all(color: Colors.black54, width: 1),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Color(0xFF1A1A1A),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(icon, color: iconColor ?? Colors.white70, size: 20),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(title, style: _getTextStyle(themeType, const TextStyle(color: Colors.white70, fontSize: 11, fontWeight: FontWeight.bold, letterSpacing: 1.0))),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AudioCablesPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = const Color(0xFF151515)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 8.0
|
||||
..strokeCap = StrokeCap.round;
|
||||
|
||||
final highlight = Paint()
|
||||
..color = const Color(0xFF3A3A3A)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2.0
|
||||
..strokeCap = StrokeCap.round;
|
||||
|
||||
void drawCable(Path path) {
|
||||
canvas.drawPath(path, paint);
|
||||
canvas.drawPath(path, highlight);
|
||||
}
|
||||
|
||||
// Cable 1
|
||||
Path c1 = Path()..moveTo(-20, size.height * 0.2)..quadraticBezierTo(100, size.height * 0.25, 50, size.height * 0.4)..quadraticBezierTo(0, size.height * 0.5, -20, size.height * 0.55);
|
||||
drawCable(c1);
|
||||
|
||||
// Cable 2
|
||||
Path c2 = Path()..moveTo(size.width + 20, size.height * 0.4)..quadraticBezierTo(size.width - 100, size.height * 0.5, size.width - 50, size.height * 0.7)..quadraticBezierTo(size.width, size.height * 0.8, size.width + 20, size.height * 0.85);
|
||||
drawCable(c2);
|
||||
|
||||
// Cable 3 (bottom)
|
||||
Path c3 = Path()..moveTo(size.width * 0.2, size.height + 20)..quadraticBezierTo(size.width * 0.3, size.height - 80, size.width * 0.5, size.height - 60)..quadraticBezierTo(size.width * 0.7, size.height - 40, size.width * 0.8, size.height + 20);
|
||||
drawCable(c3);
|
||||
|
||||
// Jack connector
|
||||
_drawJack(canvas, Offset(80, size.height * 0.38), -0.5);
|
||||
_drawJack(canvas, Offset(size.width - 60, size.height * 0.68), 0.8);
|
||||
}
|
||||
|
||||
void _drawJack(Canvas canvas, Offset pos, double angle) {
|
||||
canvas.save();
|
||||
canvas.translate(pos.dx, pos.dy);
|
||||
canvas.rotate(angle);
|
||||
|
||||
// Cable end
|
||||
canvas.drawRect(const Rect.fromLTWH(-15, -4, 15, 8), Paint()..color = const Color(0xFF151515));
|
||||
|
||||
// Base plastic
|
||||
canvas.drawRRect(RRect.fromRectAndRadius(const Rect.fromLTWH(0, -6, 25, 12), const Radius.circular(2)), Paint()..color = const Color(0xFF222222));
|
||||
canvas.drawRRect(RRect.fromRectAndRadius(const Rect.fromLTWH(2, -4, 21, 8), const Radius.circular(2)), Paint()..color = const Color(0xFF444444));
|
||||
|
||||
// Metal pin
|
||||
canvas.drawRect(const Rect.fromLTWH(25, -2, 15, 4), Paint()..color = const Color(0xFFCCCCCC));
|
||||
canvas.drawRect(const Rect.fromLTWH(40, -1.5, 5, 3), Paint()..color = const Color(0xFFAAAAAA)); // tip
|
||||
|
||||
// Rings
|
||||
canvas.drawLine(const Offset(30, -2), const Offset(30, 2), Paint()..color = Colors.black..strokeWidth = 1.5);
|
||||
canvas.drawLine(const Offset(35, -2), const Offset(35, 2), Paint()..color = Colors.black..strokeWidth = 1.5);
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
||||
|
||||
// --- WIDGETS STANDARD ---
|
||||
|
||||
class _FeatureCard extends StatelessWidget {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
|
|
|
|||
|
|
@ -78,6 +78,15 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
requiredLevel: 15,
|
||||
currentLevel: playerLevel,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
_ThemeCard(
|
||||
title: "Musica",
|
||||
subtitle: "Vinili, cassette e vibrazioni sonore",
|
||||
type: AppThemeType.music,
|
||||
previewColors: AppColors.music,
|
||||
requiredLevel: 20, // Tema Esclusivo di Livello 20!
|
||||
currentLevel: playerLevel,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue