Compare commits
3 commits
ff6797c7e8
...
ea54bf3a12
| Author | SHA1 | Date | |
|---|---|---|---|
| ea54bf3a12 | |||
| 8ef703c082 | |||
| 6e5fb8a984 |
9 changed files with 731 additions and 625 deletions
BIN
assets/.DS_Store
vendored
BIN
assets/.DS_Store
vendored
Binary file not shown.
BIN
assets/images/arcade.jpg
Normal file
BIN
assets/images/arcade.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
BIN
assets/images/grimorio.jpg
Normal file
BIN
assets/images/grimorio.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 MiB |
|
|
@ -5,7 +5,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
enum AppThemeType { doodle, wood, cyberpunk, arcade, grimorio, music } // <-- Aggiunto 'music'
|
enum AppThemeType { doodle, wood, cyberpunk, arcade, grimorio, music }
|
||||||
|
|
||||||
class ThemeColors {
|
class ThemeColors {
|
||||||
final Color background;
|
final Color background;
|
||||||
|
|
@ -45,16 +45,15 @@ class AppColors {
|
||||||
);
|
);
|
||||||
|
|
||||||
static const ThemeColors grimorio = ThemeColors(
|
static const ThemeColors grimorio = ThemeColors(
|
||||||
background: Color(0xFF1E112A), gridLine: Color(0xFF8D6E63),
|
background: Color(0xFF1E112A), gridLine: Colors.black, // <--- Modificato in nero!
|
||||||
playerRed: Color(0xFFE91E63), playerBlue: Color(0xFF4FC3F7), text: Color(0xFFFFF3E0),
|
playerRed: Color(0xFFE91E63), playerBlue: Color(0xFF4FC3F7), text: Color(0xFFFFF3E0),
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- NUOVO TEMA MUSICA ---
|
|
||||||
static const ThemeColors music = ThemeColors(
|
static const ThemeColors music = ThemeColors(
|
||||||
background: Color(0xFF120B29), // Viola scuro (stile Synthwave)
|
background: Color(0xFF120B29),
|
||||||
gridLine: Color(0xFF6A1B9A), // Viola elettrico
|
gridLine: Color(0xFF6A1B9A),
|
||||||
playerRed: Color(0xFFFF2A6D), // Rosa acceso
|
playerRed: Color(0xFFFF2A6D),
|
||||||
playerBlue: Color(0xFF05D5FF), // Ciano
|
playerBlue: Color(0xFF05D5FF),
|
||||||
text: Color(0xFFE0E0E0),
|
text: Color(0xFFE0E0E0),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -78,7 +77,7 @@ class ThemeIcons {
|
||||||
case AppThemeType.cyberpunk: return FontAwesomeIcons.microchip;
|
case AppThemeType.cyberpunk: return FontAwesomeIcons.microchip;
|
||||||
case AppThemeType.arcade: return FontAwesomeIcons.coins;
|
case AppThemeType.arcade: return FontAwesomeIcons.coins;
|
||||||
case AppThemeType.grimorio: return FontAwesomeIcons.crown;
|
case AppThemeType.grimorio: return FontAwesomeIcons.crown;
|
||||||
case AppThemeType.music: return FontAwesomeIcons.compactDisc; // CD/Vinile per i punti
|
case AppThemeType.music: return FontAwesomeIcons.compactDisc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +88,7 @@ class ThemeIcons {
|
||||||
case AppThemeType.cyberpunk: return FontAwesomeIcons.bug;
|
case AppThemeType.cyberpunk: return FontAwesomeIcons.bug;
|
||||||
case AppThemeType.arcade: return FontAwesomeIcons.ghost;
|
case AppThemeType.arcade: return FontAwesomeIcons.ghost;
|
||||||
case AppThemeType.grimorio: return FontAwesomeIcons.hatWizard;
|
case AppThemeType.grimorio: return FontAwesomeIcons.hatWizard;
|
||||||
case AppThemeType.music: return FontAwesomeIcons.volumeXmark; // Muto/Errore per la bomba
|
case AppThemeType.music: return FontAwesomeIcons.volumeXmark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,7 +99,7 @@ class ThemeIcons {
|
||||||
case AppThemeType.cyberpunk: return FontAwesomeIcons.networkWired;
|
case AppThemeType.cyberpunk: return FontAwesomeIcons.networkWired;
|
||||||
case AppThemeType.arcade: return FontAwesomeIcons.shuffle;
|
case AppThemeType.arcade: return FontAwesomeIcons.shuffle;
|
||||||
case AppThemeType.grimorio: return FontAwesomeIcons.hurricane;
|
case AppThemeType.grimorio: return FontAwesomeIcons.hurricane;
|
||||||
case AppThemeType.music: return FontAwesomeIcons.sliders; // Fader da DJ
|
case AppThemeType.music: return FontAwesomeIcons.sliders;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +110,7 @@ class ThemeIcons {
|
||||||
case AppThemeType.cyberpunk: return FontAwesomeIcons.robot;
|
case AppThemeType.cyberpunk: return FontAwesomeIcons.robot;
|
||||||
case AppThemeType.arcade: return FontAwesomeIcons.gamepad;
|
case AppThemeType.arcade: return FontAwesomeIcons.gamepad;
|
||||||
case AppThemeType.grimorio: return FontAwesomeIcons.masksTheater;
|
case AppThemeType.grimorio: return FontAwesomeIcons.masksTheater;
|
||||||
case AppThemeType.music: return FontAwesomeIcons.headphones; // Cuffie per il Jolly
|
case AppThemeType.music: return FontAwesomeIcons.headphones;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,17 +121,17 @@ class ThemeIcons {
|
||||||
case AppThemeType.cyberpunk: return FontAwesomeIcons.shieldHalved;
|
case AppThemeType.cyberpunk: return FontAwesomeIcons.shieldHalved;
|
||||||
case AppThemeType.arcade: return FontAwesomeIcons.powerOff;
|
case AppThemeType.arcade: return FontAwesomeIcons.powerOff;
|
||||||
case AppThemeType.grimorio: return FontAwesomeIcons.meteor;
|
case AppThemeType.grimorio: return FontAwesomeIcons.meteor;
|
||||||
case AppThemeType.music: return FontAwesomeIcons.pause; // Pausa per il blocco vuoto
|
case AppThemeType.music: return FontAwesomeIcons.pause;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static IconData ice(AppThemeType type) {
|
static IconData ice(AppThemeType type) {
|
||||||
if (type == AppThemeType.music) return FontAwesomeIcons.music; // Nota musicale ghiacciata
|
if (type == AppThemeType.music) return FontAwesomeIcons.music;
|
||||||
return FontAwesomeIcons.snowflake;
|
return FontAwesomeIcons.snowflake;
|
||||||
}
|
}
|
||||||
|
|
||||||
static IconData multiplier(AppThemeType type) {
|
static IconData multiplier(AppThemeType type) {
|
||||||
if (type == AppThemeType.music) return FontAwesomeIcons.forwardFast; // Fast Forward per il x2
|
if (type == AppThemeType.music) return FontAwesomeIcons.forwardFast;
|
||||||
return FontAwesomeIcons.bolt;
|
return FontAwesomeIcons.bolt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -54,28 +54,72 @@ class BoardPainter extends CustomPainter {
|
||||||
double offset = spacing / 2;
|
double offset = spacing / 2;
|
||||||
Offset getScreenPos(int x, int y) => Offset(x * spacing + offset, y * spacing + offset);
|
Offset getScreenPos(int x, int y) => Offset(x * spacing + offset, y * spacing + offset);
|
||||||
|
|
||||||
// --- NUOVO SFONDO LUMINOSO SAGOMATO (Solo per tema Musica) ---
|
// =======================================================================
|
||||||
if (themeType == AppThemeType.music) {
|
// 1. CREAZIONE DELLA SAGOMA DELL'ARENA (SFONDO E BORDO)
|
||||||
Path arenaShape = Path();
|
// =======================================================================
|
||||||
// Uniamo la forma di ogni box giocabile per creare il tappeto
|
Path arenaShape = Path();
|
||||||
for (var box in board.boxes) {
|
bool isFirst = true;
|
||||||
if (box.type != BoxType.invisible) { // Ignora i buchi
|
|
||||||
Offset p1 = getScreenPos(box.x, box.y);
|
// Uniamo la forma di ogni box giocabile per creare un'unica sagoma
|
||||||
Offset p2 = getScreenPos(box.x + 1, box.y + 1);
|
for (var box in board.boxes) {
|
||||||
arenaShape.addRect(Rect.fromPoints(p1, p2));
|
if (box.type != BoxType.invisible) { // Ignora i buchi
|
||||||
|
Offset p1 = getScreenPos(box.x, box.y);
|
||||||
|
Offset p2 = getScreenPos(box.x + 1, box.y + 1);
|
||||||
|
Path boxPath = Path()..addRect(Rect.fromPoints(p1, p2));
|
||||||
|
|
||||||
|
if (isFirst) {
|
||||||
|
arenaShape = boxPath;
|
||||||
|
isFirst = false;
|
||||||
|
} else {
|
||||||
|
arenaShape = Path.combine(PathOperation.union, arenaShape, boxPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disegniamo lo sfondo unito, chiaro e con un po' di blur
|
|
||||||
canvas.drawPath(
|
|
||||||
arenaShape,
|
|
||||||
Paint()
|
|
||||||
..color = Colors.white.withOpacity(0.08) // Colore chiarissimo
|
|
||||||
..style = PaintingStyle.fill
|
|
||||||
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10.0), // Effetto stacco/glow
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// -----------------------------------------------------------
|
|
||||||
|
// --- DISEGNO DELLO SFONDO LUMINOSO ---
|
||||||
|
final fillPaint = Paint()
|
||||||
|
..style = PaintingStyle.fill
|
||||||
|
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10.0);
|
||||||
|
|
||||||
|
if (themeType == AppThemeType.music) {
|
||||||
|
fillPaint.color = Colors.white.withOpacity(0.08);
|
||||||
|
canvas.drawPath(arenaShape, fillPaint);
|
||||||
|
} else if (themeType == AppThemeType.wood) {
|
||||||
|
fillPaint.color = Colors.black.withOpacity(0.3);
|
||||||
|
fillPaint.maskFilter = const MaskFilter.blur(BlurStyle.normal, 15.0);
|
||||||
|
canvas.drawPath(arenaShape, fillPaint);
|
||||||
|
} else if (themeType == AppThemeType.cyberpunk) {
|
||||||
|
fillPaint.color = theme.playerBlue.withOpacity(0.1);
|
||||||
|
canvas.drawPath(arenaShape, fillPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DISEGNO DEL BORDO ESTERNO SOTTILE ---
|
||||||
|
double baseStroke = themeType == AppThemeType.grimorio ? 6.0 : 4.0;
|
||||||
|
if (themeType == AppThemeType.doodle) baseStroke = 2.5;
|
||||||
|
|
||||||
|
final outlinePaint = Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = baseStroke * 0.5 // Moltiplicato per 0.5 = grande la metà delle linee interne!
|
||||||
|
..strokeJoin = StrokeJoin.round;
|
||||||
|
|
||||||
|
if (themeType == AppThemeType.cyberpunk) {
|
||||||
|
outlinePaint.color = theme.gridLine;
|
||||||
|
outlinePaint.maskFilter = MaskFilter.blur(BlurStyle.solid, 4.0 * blinkValue.clamp(0.1, 1.0));
|
||||||
|
}
|
||||||
|
else if (themeType == AppThemeType.arcade) { outlinePaint.color = Colors.white; }
|
||||||
|
else if (themeType == AppThemeType.grimorio) { outlinePaint.color = theme.gridLine.withOpacity(0.6); }
|
||||||
|
else if (themeType == AppThemeType.music) { outlinePaint.color = Colors.black; } // Rimosso lo spessore forzato a 8.0!
|
||||||
|
else if (themeType == AppThemeType.doodle) { outlinePaint.color = const Color(0xFF111122); } // Rimosso lo spessore forzato a 6.0!
|
||||||
|
else if (themeType == AppThemeType.wood) {
|
||||||
|
outlinePaint.color = const Color(0xFF3E2723);
|
||||||
|
outlinePaint.maskFilter = const MaskFilter.blur(BlurStyle.normal, 2.0);
|
||||||
|
}
|
||||||
|
else { outlinePaint.color = theme.gridLine.withOpacity(0.8); }
|
||||||
|
|
||||||
|
// Disegniamo il contorno
|
||||||
|
canvas.drawPath(arenaShape, outlinePaint);
|
||||||
|
// =======================================================================
|
||||||
|
|
||||||
|
|
||||||
for (var box in board.boxes) {
|
for (var box in board.boxes) {
|
||||||
Offset p1 = getScreenPos(box.x, box.y);
|
Offset p1 = getScreenPos(box.x, box.y);
|
||||||
|
|
|
||||||
|
|
@ -291,9 +291,11 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
|
|
||||||
String? bgImage;
|
String? bgImage;
|
||||||
if (themeType == AppThemeType.wood) bgImage = 'assets/images/wood_bg.jpg';
|
if (themeType == AppThemeType.wood) bgImage = 'assets/images/wood_bg.jpg';
|
||||||
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg'; // Questo è lo sfondo carta
|
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg';
|
||||||
if (themeType == AppThemeType.cyberpunk) bgImage = 'assets/images/cyber_bg.jpg';
|
if (themeType == AppThemeType.cyberpunk) bgImage = 'assets/images/cyber_bg.jpg';
|
||||||
if (themeType == AppThemeType.music) bgImage = 'assets/images/music_bg.jpg';
|
if (themeType == AppThemeType.music) bgImage = 'assets/images/music_bg.jpg';
|
||||||
|
if (themeType == AppThemeType.arcade) bgImage = 'assets/images/arcade.jpg';
|
||||||
|
if (themeType == AppThemeType.grimorio) bgImage = 'assets/images/grimorio.jpg';
|
||||||
|
|
||||||
Color indicatorColor = themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? Colors.white : Colors.black;
|
Color indicatorColor = themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? Colors.white : Colors.black;
|
||||||
|
|
||||||
|
|
@ -326,7 +328,8 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(10.0),
|
// PADDING RIDOTTO AL MINIMO: permette alla griglia di guadagnare pixel preziosi per allargarsi/alzarsi!
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 2.0, vertical: 2.0),
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
int cols = gameController.board.columns + 1;
|
int cols = gameController.board.columns + 1;
|
||||||
|
|
@ -341,19 +344,20 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
width: actualWidth, height: actualHeight,
|
width: actualWidth, height: actualHeight,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
// --- IL VERO SFONDO SFOCATO SAGOMATO ---
|
// --- IL VERO SFONDO SFOCATO SAGOMATO (ORA PER TUTTI I TEMI) ---
|
||||||
if (themeType == AppThemeType.music)
|
Positioned.fill(
|
||||||
Positioned.fill(
|
child: ClipPath(
|
||||||
child: ClipPath(
|
clipper: _ArenaClipper(gameController.board),
|
||||||
clipper: _ArenaClipper(gameController.board),
|
child: BackdropFilter(
|
||||||
child: BackdropFilter(
|
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
|
||||||
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
|
child: Container(
|
||||||
child: Container(
|
color: themeType == AppThemeType.doodle
|
||||||
color: Colors.white.withOpacity(0.08),
|
? Colors.black.withOpacity(0.05)
|
||||||
),
|
: Colors.white.withOpacity(0.12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
|
|
@ -383,7 +387,8 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
),
|
),
|
||||||
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 20.0, left: 20.0, right: 20.0),
|
// PADDING RIDOTTO IN BASSO: 10 al posto di 20, per far esplodere la griglia verticalmente
|
||||||
|
padding: const EdgeInsets.only(bottom: 10.0, left: 20.0, right: 20.0, top: 5.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -429,23 +434,13 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
canPop: true,
|
canPop: true,
|
||||||
onPopInvoked: (didPop) { gameController.disconnectOnlineGame(); },
|
onPopInvoked: (didPop) { gameController.disconnectOnlineGame(); },
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Colors.transparent, // Assicuriamo che la scaffold base sia trasparente
|
backgroundColor: themeType == AppThemeType.doodle ? Colors.white : (bgImage != null ? Colors.transparent : theme.background),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
// 1. Sfondo base a tinta unita (In caso non ci sia l'immagine)
|
// 1. Sfondo base a tinta unita
|
||||||
Container(color: themeType == AppThemeType.doodle ? Colors.white : theme.background),
|
Container(color: themeType == AppThemeType.doodle ? Colors.white : theme.background),
|
||||||
|
|
||||||
// 2. Immagine di Sfondo per tutti i temi che la supportano
|
// 2. Griglia a quadretti (Doodle Theme)
|
||||||
if (bgImage != null)
|
|
||||||
Positioned.fill(
|
|
||||||
child: Image.asset(
|
|
||||||
bgImage,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// 3. Griglia a righe incrociate per il doodle (Sopra l'immagine di carta)
|
|
||||||
if (themeType == AppThemeType.doodle)
|
if (themeType == AppThemeType.doodle)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
|
|
@ -453,8 +448,20 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// 4. Patina scura (Cyberpunk e Music) per far risaltare il neon
|
// 3. Immagine di Sfondo
|
||||||
if (bgImage != null && (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music))
|
if (bgImage != null)
|
||||||
|
Positioned.fill(
|
||||||
|
child: Image.asset(
|
||||||
|
bgImage,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
color: themeType == AppThemeType.doodle ? Colors.white.withOpacity(0.5) : null,
|
||||||
|
colorBlendMode: themeType == AppThemeType.doodle ? BlendMode.lighten : null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 4. Patina scura (Cyberpunk, Music, Arcade e Grimorio)
|
||||||
|
if (bgImage != null && (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music || themeType == AppThemeType.arcade || themeType == AppThemeType.grimorio))
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// FILE: lib/ui/home/dialogs/dialog.dart
|
// FILE: lib/ui/home/dialog.dart
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -8,11 +8,11 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
|
||||||
import '../../../core/theme_manager.dart';
|
import '../../core/theme_manager.dart';
|
||||||
import '../../../core/app_colors.dart';
|
import '../../core/app_colors.dart';
|
||||||
import '../../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
import '../../../widgets/painters.dart';
|
import '../../widgets/painters.dart';
|
||||||
import '../../../widgets/cyber_border.dart';
|
import '../../widgets/cyber_border.dart';
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// 1. DIALOGO MISSIONI (QUESTS)
|
// 1. DIALOGO MISSIONI (QUESTS)
|
||||||
|
|
@ -158,14 +158,29 @@ class LeaderboardDialog extends StatelessWidget {
|
||||||
return Center(child: Text("Ancora nessun campione...", style: TextStyle(color: theme.text.withOpacity(0.5))));
|
return Center(child: Text("Ancora nessun campione...", style: TextStyle(color: theme.text.withOpacity(0.5))));
|
||||||
}
|
}
|
||||||
|
|
||||||
final docs = snapshot.data!.docs;
|
// 1. ESTRAIAMO TUTTI I DOCUMENTI
|
||||||
|
final rawDocs = snapshot.data!.docs;
|
||||||
|
|
||||||
|
// 2. APPLICHIAMO IL FILTRO PER NASCONDERE "PAOLO"
|
||||||
|
final filteredDocs = rawDocs.where((doc) {
|
||||||
|
var data = doc.data() as Map<String, dynamic>;
|
||||||
|
String name = (data['name'] ?? '').toString().toUpperCase();
|
||||||
|
return name != 'PAOLO';
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
// 3. SE DOPO IL FILTRO NON C'E' NESSUNO
|
||||||
|
if (filteredDocs.isEmpty) {
|
||||||
|
return Center(child: Text("Ancora nessun campione...", style: TextStyle(color: theme.text.withOpacity(0.5))));
|
||||||
|
}
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
itemCount: docs.length,
|
itemCount: filteredDocs.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
var data = docs[index].data() as Map<String, dynamic>;
|
var doc = filteredDocs[index];
|
||||||
|
var data = doc.data() as Map<String, dynamic>;
|
||||||
String? myUid = FirebaseAuth.instance.currentUser?.uid;
|
String? myUid = FirebaseAuth.instance.currentUser?.uid;
|
||||||
bool isMe = docs[index].id == myUid;
|
bool isMe = doc.id == myUid;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
|
|
@ -230,9 +245,50 @@ class TutorialDialog extends StatelessWidget {
|
||||||
final themeType = themeManager.currentThemeType;
|
final themeType = themeManager.currentThemeType;
|
||||||
Color inkColor = const Color(0xFF111122);
|
Color inkColor = const Color(0xFF111122);
|
||||||
|
|
||||||
String goldLabel = themeType == AppThemeType.grimorio ? "CORONA:" : "ORO:";
|
// ETICHETTE DINAMICHE PER I POTENZIAMENTI
|
||||||
String bombLabel = themeType == AppThemeType.grimorio ? "STREGA:" : "BOMBA:";
|
String goldLabel = "ORO:";
|
||||||
String jokerLabel = themeType == AppThemeType.grimorio ? "GIULLARE:" : "JOLLY:";
|
String bombLabel = "BOMBA:";
|
||||||
|
String swapLabel = "SCAMBIO:";
|
||||||
|
String jokerLabel = "JOLLY:";
|
||||||
|
String iceLabel = "GHIACCIO:";
|
||||||
|
String multiplierLabel = "x2:";
|
||||||
|
String blockLabel = "BUCO NERO:";
|
||||||
|
|
||||||
|
if (themeType == AppThemeType.grimorio) {
|
||||||
|
goldLabel = "CORONA:";
|
||||||
|
bombLabel = "STREGA:";
|
||||||
|
jokerLabel = "GIULLARE:";
|
||||||
|
swapLabel = "TORNADO:";
|
||||||
|
multiplierLabel = "FULMINE:";
|
||||||
|
blockLabel = "METEORITE:";
|
||||||
|
} else if (themeType == AppThemeType.music) {
|
||||||
|
goldLabel = "DISCO D'ORO:";
|
||||||
|
bombLabel = "MUTO:";
|
||||||
|
jokerLabel = "DJ:";
|
||||||
|
swapLabel = "MIXER:";
|
||||||
|
iceLabel = "NOTA:";
|
||||||
|
multiplierLabel = "AVANTI VELOCE:";
|
||||||
|
blockLabel = "PAUSA:";
|
||||||
|
} else if (themeType == AppThemeType.arcade) {
|
||||||
|
goldLabel = "GETTONE:";
|
||||||
|
bombLabel = "FANTASMA:";
|
||||||
|
jokerLabel = "GAMEPAD:";
|
||||||
|
swapLabel = "SHUFFLE:";
|
||||||
|
blockLabel = "POWER OFF:";
|
||||||
|
} else if (themeType == AppThemeType.cyberpunk) {
|
||||||
|
goldLabel = "CHIP:";
|
||||||
|
bombLabel = "VIRUS:";
|
||||||
|
jokerLabel = "BOT:";
|
||||||
|
swapLabel = "NETWORK:";
|
||||||
|
blockLabel = "FIREWALL:";
|
||||||
|
} else if (themeType == AppThemeType.wood) {
|
||||||
|
goldLabel = "GEMMA:";
|
||||||
|
bombLabel = "FUOCO:";
|
||||||
|
jokerLabel = "CHIAVE:";
|
||||||
|
blockLabel = "DIVIETO:";
|
||||||
|
} else if (themeType == AppThemeType.doodle) {
|
||||||
|
bombLabel = "VIRUS:";
|
||||||
|
}
|
||||||
|
|
||||||
Widget dialogContent = themeType == AppThemeType.doodle
|
Widget dialogContent = themeType == AppThemeType.doodle
|
||||||
? Transform.rotate(
|
? Transform.rotate(
|
||||||
|
|
@ -260,15 +316,15 @@ class TutorialDialog extends StatelessWidget {
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.bomb(themeType), iconColor: Colors.deepPurple, text: "$bombLabel Non chiuderlo! Perderai -1 Punto.", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.bomb(themeType), iconColor: Colors.deepPurple, text: "$bombLabel Non chiuderlo! Perderai -1 Punto.", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.swap(themeType), iconColor: Colors.purpleAccent, text: "SCAMBIO: Inverte istantaneamente i punteggi dei giocatori.", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.swap(themeType), iconColor: Colors.purpleAccent, text: "$swapLabel Inverte istantaneamente i punteggi dei giocatori.", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.joker(themeType), iconColor: Colors.green.shade600, text: "$jokerLabel Scegli dove nasconderlo a inizio partita. Se lo chiudi tu +2, se lo chiude l'avversario -1!", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.joker(themeType), iconColor: Colors.green.shade600, text: "$jokerLabel Scegli dove nasconderlo a inizio partita. Se lo chiudi tu +2, se lo chiude l'avversario -1!", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.ice(themeType), iconColor: Colors.cyanAccent, text: "GHIACCIO: Devi cliccarlo due volte per poterlo rompere e chiudere.", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.ice(themeType), iconColor: Colors.cyanAccent, text: "$iceLabel Devi cliccarlo due volte per poterlo rompere e chiudere.", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.multiplier(themeType), iconColor: Colors.yellowAccent, text: "x2: Non dà punti, ma raddoppia il punteggio della prossima casella che chiudi!", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.multiplier(themeType), iconColor: Colors.yellowAccent, text: "$multiplierLabel Non dà punti, ma raddoppia il punteggio della prossima casella che chiudi!", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.block(themeType), iconColor: Colors.grey, text: "BUCO NERO: Questa casella non esiste. Se la chiudi perdi il turno.", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.block(themeType), iconColor: Colors.grey, text: "$blockLabel Questa casella non esiste. Se la chiudi perdi il turno.", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
|
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
Center(
|
Center(
|
||||||
|
|
@ -317,15 +373,15 @@ class TutorialDialog extends StatelessWidget {
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.bomb(themeType), iconColor: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.greenAccent : Colors.deepPurple, text: "$bombLabel Non chiuderlo! Perderai -1 Punto.", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.bomb(themeType), iconColor: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.greenAccent : Colors.deepPurple, text: "$bombLabel Non chiuderlo! Perderai -1 Punto.", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.swap(themeType), iconColor: Colors.purpleAccent, text: "SCAMBIO: Inverte istantaneamente i punteggi dei giocatori.", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.swap(themeType), iconColor: Colors.purpleAccent, text: "$swapLabel Inverte istantaneamente i punteggi dei giocatori.", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.joker(themeType), iconColor: theme.playerBlue, text: "$jokerLabel Scegli dove nasconderlo a inizio partita. Se lo chiudi tu +2, se lo chiude l'avversario -1!", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.joker(themeType), iconColor: theme.playerBlue, text: "$jokerLabel Scegli dove nasconderlo a inizio partita. Se lo chiudi tu +2, se lo chiude l'avversario -1!", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.ice(themeType), iconColor: Colors.cyanAccent, text: "GHIACCIO: Devi cliccarlo due volte per poterlo rompere e chiudere.", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.ice(themeType), iconColor: Colors.cyanAccent, text: "$iceLabel Devi cliccarlo due volte per poterlo rompere e chiudere.", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.multiplier(themeType), iconColor: Colors.yellowAccent, text: "x2: Non dà punti, ma raddoppia il punteggio della prossima casella che chiudi!", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.multiplier(themeType), iconColor: Colors.yellowAccent, text: "$multiplierLabel Non dà punti, ma raddoppia il punteggio della prossima casella che chiudi!", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TutorialStep(icon: ThemeIcons.block(themeType), iconColor: Colors.grey, text: "BUCO NERO: Questa casella non esiste. Se la chiudi perdi il turno.", themeType: themeType, inkColor: inkColor, theme: theme),
|
TutorialStep(icon: ThemeIcons.block(themeType), iconColor: Colors.grey, text: "$blockLabel Questa casella non esiste. Se la chiudi perdi il turno.", themeType: themeType, inkColor: inkColor, theme: theme),
|
||||||
|
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,14 @@ import 'dart:ui';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:app_links/app_links.dart';
|
import 'package:app_links/app_links.dart';
|
||||||
|
|
||||||
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import '../../logic/game_controller.dart';
|
import '../../logic/game_controller.dart';
|
||||||
import '../../core/theme_manager.dart';
|
import '../../core/theme_manager.dart';
|
||||||
import '../../core/app_colors.dart';
|
import '../../core/app_colors.dart';
|
||||||
|
|
@ -29,321 +31,9 @@ import '../../widgets/painters.dart';
|
||||||
import '../../widgets/cyber_border.dart';
|
import '../../widgets/cyber_border.dart';
|
||||||
import '../../widgets/music_theme_widgets.dart';
|
import '../../widgets/music_theme_widgets.dart';
|
||||||
import '../../widgets/home_buttons.dart';
|
import '../../widgets/home_buttons.dart';
|
||||||
|
import '../../widgets/custom_settings_button.dart';
|
||||||
import 'dialog.dart';
|
import 'dialog.dart';
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
// WIDGET LOCALI PER IL SETUP DELLA PARTITA
|
|
||||||
// ===========================================================================
|
|
||||||
class _NeonShapeButton extends StatelessWidget {
|
|
||||||
final IconData icon; final String label; final bool isSelected;
|
|
||||||
final ThemeColors theme; final AppThemeType themeType; final VoidCallback onTap;
|
|
||||||
final bool isLocked; final bool isSpecial;
|
|
||||||
|
|
||||||
const _NeonShapeButton({required this.icon, required this.label, required this.isSelected, required this.theme, required this.themeType, required this.onTap, this.isLocked = false, this.isSpecial = false});
|
|
||||||
|
|
||||||
Color _getDoodleColor() {
|
|
||||||
switch (label) {
|
|
||||||
case 'Rombo': return Colors.lightBlue.shade200;
|
|
||||||
case 'Croce': return Colors.green.shade200;
|
|
||||||
case 'Buco': return Colors.pink.shade200;
|
|
||||||
case 'Clessidra': return Colors.purple.shade200;
|
|
||||||
case 'Caos': return Colors.grey.shade300;
|
|
||||||
default: return Colors.lightBlue.shade200;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (themeType == AppThemeType.doodle) {
|
|
||||||
Color doodleColor = isLocked ? Colors.grey : _getDoodleColor();
|
|
||||||
Color inkColor = const Color(0xFF111122);
|
|
||||||
double tilt = (label.length % 2 == 0) ? -0.05 : 0.04;
|
|
||||||
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: tilt,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: isLocked ? null : onTap,
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: DoodleBackgroundPainter(fillColor: isSelected ? doodleColor : Colors.white.withOpacity(0.8), strokeColor: inkColor, seed: label.length * 3),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(isLocked ? Icons.lock : icon, color: inkColor, size: 24),
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
Text(isLocked ? "Liv. 10" : label, style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: 11, fontWeight: FontWeight.w900, letterSpacing: 0.5))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Color mainColor = isSpecial && !isLocked ? Colors.purpleAccent : theme.playerBlue;
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: isLocked ? null : onTap,
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 250), curve: Curves.easeOutCubic, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), transform: Matrix4.translationValues(0, isSelected ? 2 : 0, 0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(15), border: Border.all(color: isLocked ? Colors.transparent : (isSelected ? mainColor : Colors.white.withOpacity(0.1)), width: isSelected ? 2 : 1),
|
|
||||||
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isLocked ? [Colors.grey.withOpacity(0.1), Colors.black.withOpacity(0.2)] : isSelected ? [mainColor.withOpacity(0.3), mainColor.withOpacity(0.1)] : [theme.text.withOpacity(0.1), theme.text.withOpacity(0.02)]),
|
|
||||||
boxShadow: isLocked ? [] : isSelected ? [BoxShadow(color: mainColor.withOpacity(0.5), blurRadius: 15, spreadRadius: 1, offset: const Offset(0, 0))] : [BoxShadow(color: Colors.black.withOpacity(0.4), blurRadius: 6, offset: const Offset(2, 4)), BoxShadow(color: Colors.white.withOpacity(0.05), blurRadius: 2, offset: const Offset(-1, -1))],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(isLocked ? Icons.lock : icon, color: isLocked ? Colors.grey.withOpacity(0.5) : (isSelected ? Colors.white : theme.text.withOpacity(0.6)), size: 24),
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
Text(isLocked ? "Liv. 10" : label, style: getSharedTextStyle(themeType, TextStyle(color: isLocked ? Colors.grey.withOpacity(0.5) : (isSelected ? Colors.white : theme.text.withOpacity(0.6)), fontSize: 11, fontWeight: isSelected ? FontWeight.w900 : FontWeight.bold))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NeonSizeButton extends StatelessWidget {
|
|
||||||
final String label; final bool isSelected; final ThemeColors theme; final AppThemeType themeType; final VoidCallback onTap;
|
|
||||||
const _NeonSizeButton({required this.label, required this.isSelected, required this.theme, required this.themeType, required this.onTap});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (themeType == AppThemeType.doodle) {
|
|
||||||
Color doodleColor = label == 'MAX' ? Colors.red.shade200 : Colors.cyan.shade100; Color inkColor = const Color(0xFF111122); double tilt = (label == 'M' || label == 'MAX') ? 0.05 : -0.04;
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: tilt,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: CustomPaint(painter: DoodleBackgroundPainter(fillColor: isSelected ? doodleColor : Colors.white.withOpacity(0.8), strokeColor: inkColor, seed: label.codeUnitAt(0), isCircle: true), child: SizedBox(width: 50, height: 50, child: Center(child: Text(label, style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: 18, fontWeight: FontWeight.w900)))))),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 250), curve: Curves.easeOutCubic, width: 50, height: 50, transform: Matrix4.translationValues(0, isSelected ? 2 : 0, 0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle, border: Border.all(color: isSelected ? theme.playerRed : Colors.white.withOpacity(0.1), width: isSelected ? 2 : 1),
|
|
||||||
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isSelected ? [theme.playerRed.withOpacity(0.3), theme.playerRed.withOpacity(0.1)] : [theme.text.withOpacity(0.1), theme.text.withOpacity(0.02)]),
|
|
||||||
boxShadow: isSelected ? [BoxShadow(color: theme.playerRed.withOpacity(0.5), blurRadius: 15, spreadRadius: 1)] : [BoxShadow(color: Colors.black.withOpacity(0.4), blurRadius: 6, offset: const Offset(2, 4)), BoxShadow(color: Colors.white.withOpacity(0.05), blurRadius: 2, offset: const Offset(-1, -1))],
|
|
||||||
),
|
|
||||||
child: Center(child: Text(label, style: getSharedTextStyle(themeType, TextStyle(color: isSelected ? Colors.white : theme.text.withOpacity(0.6), fontSize: 14, fontWeight: isSelected ? FontWeight.w900 : FontWeight.bold)))),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NeonTimeSwitch extends StatelessWidget {
|
|
||||||
final bool isTimeMode; final ThemeColors theme; final AppThemeType themeType; final VoidCallback onTap;
|
|
||||||
const _NeonTimeSwitch({required this.isTimeMode, required this.theme, required this.themeType, required this.onTap});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (themeType == AppThemeType.doodle) {
|
|
||||||
Color doodleColor = Colors.orange.shade200; Color inkColor = const Color(0xFF111122);
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: -0.015,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: DoodleBackgroundPainter(fillColor: isTimeMode ? doodleColor : Colors.white.withOpacity(0.8), strokeColor: inkColor, seed: 42),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(isTimeMode ? Icons.timer : Icons.timer_off, color: inkColor, size: 28), const SizedBox(width: 12),
|
|
||||||
Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [Text(isTimeMode ? 'A TEMPO' : 'RELAX', style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontWeight: FontWeight.w900, fontSize: 16, letterSpacing: 2.0))), Text(isTimeMode ? '15 sec a mossa' : 'Nessun limite', style: getSharedTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.8), fontSize: 13, fontWeight: FontWeight.bold)))]),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(20), border: Border.all(color: isTimeMode ? Colors.amber : Colors.white.withOpacity(0.1), width: isTimeMode ? 2 : 1),
|
|
||||||
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isTimeMode ? [Colors.amber.withOpacity(0.25), Colors.amber.withOpacity(0.05)] : [theme.text.withOpacity(0.1), theme.text.withOpacity(0.02)]),
|
|
||||||
boxShadow: isTimeMode ? [BoxShadow(color: Colors.amber.withOpacity(0.3), blurRadius: 15, spreadRadius: 2)] : [BoxShadow(color: Colors.black.withOpacity(0.4), blurRadius: 6, offset: const Offset(2, 4)), BoxShadow(color: Colors.white.withOpacity(0.05), blurRadius: 2, offset: const Offset(-1, -1))],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(isTimeMode ? Icons.timer : Icons.timer_off, color: isTimeMode ? Colors.amber : theme.text.withOpacity(0.5), size: 28), const SizedBox(width: 12),
|
|
||||||
Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [Text(isTimeMode ? 'A TEMPO' : 'RELAX', style: getSharedTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.white : theme.text.withOpacity(0.5), fontWeight: FontWeight.w900, fontSize: 14, letterSpacing: 1.5))), Text(isTimeMode ? '15 sec a mossa' : 'Nessun limite', style: getSharedTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.amber.shade200 : theme.text.withOpacity(0.4), fontSize: 11, fontWeight: FontWeight.bold)))]),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NeonPrivacySwitch extends StatelessWidget {
|
|
||||||
final bool isPublic;
|
|
||||||
final ThemeColors theme;
|
|
||||||
final AppThemeType themeType;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
const _NeonPrivacySwitch({required this.isPublic, required this.theme, required this.themeType, required this.onTap});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (themeType == AppThemeType.doodle) {
|
|
||||||
Color doodleColor = isPublic ? Colors.green.shade600 : Colors.red.shade600;
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: 0.015,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
||||||
transform: Matrix4.translationValues(0, isPublic ? 3 : 0, 0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: isPublic ? doodleColor : Colors.white,
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(15), topRight: Radius.circular(8),
|
|
||||||
bottomLeft: Radius.circular(6), bottomRight: Radius.circular(15),
|
|
||||||
),
|
|
||||||
border: Border.all(color: isPublic ? theme.text : doodleColor.withOpacity(0.5), width: 2.5),
|
|
||||||
boxShadow: [BoxShadow(color: isPublic ? theme.text.withOpacity(0.8) : doodleColor.withOpacity(0.2), offset: const Offset(4, 5), blurRadius: 0)],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(isPublic ? Icons.public : Icons.lock, color: isPublic ? Colors.white : doodleColor, size: 20),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(isPublic ? 'PUBBLICA' : 'PRIVATA', style: getSharedTextStyle(themeType, TextStyle(color: isPublic ? Colors.white : doodleColor, fontWeight: FontWeight.w900, fontSize: 12, letterSpacing: 1.0))),
|
|
||||||
Text(isPublic ? 'In Bacheca' : 'Solo Codice', style: getSharedTextStyle(themeType, TextStyle(color: isPublic ? Colors.white : doodleColor.withOpacity(0.8), fontSize: 9, fontWeight: FontWeight.bold))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
colors: isPublic
|
|
||||||
? [Colors.greenAccent.withOpacity(0.25), Colors.greenAccent.withOpacity(0.05)]
|
|
||||||
: [theme.playerRed.withOpacity(0.25), theme.playerRed.withOpacity(0.05)],
|
|
||||||
),
|
|
||||||
border: Border.all(color: isPublic ? Colors.greenAccent : theme.playerRed, width: isPublic ? 2 : 1),
|
|
||||||
boxShadow: isPublic
|
|
||||||
? [BoxShadow(color: Colors.greenAccent.withOpacity(0.3), blurRadius: 15, spreadRadius: 2)]
|
|
||||||
: [BoxShadow(color: Colors.black.withOpacity(0.4), blurRadius: 6, offset: const Offset(2, 4))],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(isPublic ? Icons.public : Icons.lock, color: isPublic ? Colors.greenAccent : theme.playerRed, size: 20),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(isPublic ? 'PUBBLICA' : 'PRIVATA', style: getSharedTextStyle(themeType, TextStyle(color: isPublic ? Colors.white : theme.text.withOpacity(0.8), fontWeight: FontWeight.w900, fontSize: 11, letterSpacing: 1.5))),
|
|
||||||
Text(isPublic ? 'Tutti ti vedono' : 'Solo con Codice', style: getSharedTextStyle(themeType, TextStyle(color: isPublic ? Colors.greenAccent.shade200 : theme.playerRed.withOpacity(0.7), fontSize: 9, fontWeight: FontWeight.bold))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NeonActionButton extends StatelessWidget {
|
|
||||||
final String label;
|
|
||||||
final Color color;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final ThemeColors theme;
|
|
||||||
final AppThemeType themeType;
|
|
||||||
|
|
||||||
const _NeonActionButton({required this.label, required this.color, required this.onTap, required this.theme, required this.themeType});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (themeType == AppThemeType.doodle) {
|
|
||||||
double tilt = (label == "UNISCITI" || label == "ANNULLA") ? -0.015 : 0.02;
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: tilt,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Container(
|
|
||||||
height: 50,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: color,
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(10), topRight: Radius.circular(20),
|
|
||||||
bottomLeft: Radius.circular(25), bottomRight: Radius.circular(10),
|
|
||||||
),
|
|
||||||
border: Border.all(color: theme.text, width: 3.0),
|
|
||||||
boxShadow: [BoxShadow(color: theme.text.withOpacity(0.9), offset: const Offset(4, 4), blurRadius: 0)],
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
|
||||||
child: Text(label, style: getSharedTextStyle(themeType, TextStyle(fontSize: 20, fontWeight: FontWeight.w900, letterSpacing: 3.0, color: Colors.white))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Container(
|
|
||||||
height: 50,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [color.withOpacity(0.9), color.withOpacity(0.6)]),
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
border: Border.all(color: Colors.white.withOpacity(0.3), width: 1.5),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(color: Colors.black.withOpacity(0.5), offset: const Offset(4, 8), blurRadius: 12),
|
|
||||||
BoxShadow(color: color.withOpacity(0.3), offset: const Offset(0, 0), blurRadius: 15, spreadRadius: 1),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
|
||||||
child: Text(label, style: getSharedTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 2.0, color: Colors.white, shadows: const [Shadow(color: Colors.black, blurRadius: 2, offset: Offset(1, 1))]))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// CLASSE PRINCIPALE HOME
|
// CLASSE PRINCIPALE HOME
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
@ -511,10 +201,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
|
|
||||||
void _showError(String message) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message, style: const TextStyle(color: Colors.white)), backgroundColor: Colors.red)); }
|
void _showError(String message) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message, style: const TextStyle(color: Colors.white)), backgroundColor: Colors.red)); }
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
// DIALOGHI IN-FILE
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
void _showWaitingDialog(String code) {
|
void _showWaitingDialog(String code) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -644,8 +330,14 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- FINESTRA DI REGISTRAZIONE/LOGIN ---
|
||||||
void _showNameDialog() {
|
void _showNameDialog() {
|
||||||
final TextEditingController nameController = TextEditingController(text: StorageService.instance.playerName);
|
final TextEditingController nameController = TextEditingController(text: StorageService.instance.playerName);
|
||||||
|
final TextEditingController passController = TextEditingController();
|
||||||
|
bool isLoadingAuth = false;
|
||||||
|
bool _obscurePassword = true;
|
||||||
|
String _errorMessage = "";
|
||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context, barrierDismissible: false, barrierColor: Colors.black.withOpacity(0.8),
|
context: context, barrierDismissible: false, barrierColor: Colors.black.withOpacity(0.8),
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
|
@ -653,75 +345,245 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
final theme = themeManager.currentColors; final themeType = themeManager.currentThemeType;
|
final theme = themeManager.currentColors; final themeType = themeManager.currentThemeType;
|
||||||
Color inkColor = const Color(0xFF111122); final loc = AppLocalizations.of(context)!;
|
Color inkColor = const Color(0xFF111122); final loc = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
Widget dialogContent = themeType == AppThemeType.doodle
|
return StatefulBuilder(
|
||||||
? CustomPaint(
|
builder: (context, setStateDialog) {
|
||||||
painter: DoodleBackgroundPainter(fillColor: Colors.yellow.shade100, strokeColor: inkColor, seed: 100),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 25.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(loc.welcomeTitle, style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontWeight: FontWeight.w900, fontSize: 28, letterSpacing: 2.0)), textAlign: TextAlign.center),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Text('Scegli il tuo nome da battaglia', style: getSharedTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.8), fontSize: 18)), textAlign: TextAlign.center),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
TextField(
|
|
||||||
controller: nameController, textCapitalization: TextCapitalization.characters, textAlign: TextAlign.center, maxLength: 5,
|
|
||||||
style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: 36, fontWeight: FontWeight.bold, letterSpacing: 8)),
|
|
||||||
decoration: InputDecoration(hintText: loc.nameHint, hintStyle: getSharedTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.3), letterSpacing: 4)), filled: false, counterText: "", enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: inkColor, width: 3)), focusedBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.red.shade200, width: 5))),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
final name = nameController.text.trim();
|
|
||||||
if (name.isNotEmpty) { StorageService.instance.savePlayerName(name); Navigator.of(context).pop(); setState(() {}); }
|
|
||||||
},
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: DoodleBackgroundPainter(fillColor: Colors.green.shade200, strokeColor: inkColor, seed: 101),
|
|
||||||
child: Container(width: double.infinity, height: 60, alignment: Alignment.center, child: Text(loc.saveAndPlay, style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: 22, fontWeight: FontWeight.bold, letterSpacing: 1.5)))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Container(
|
|
||||||
decoration: BoxDecoration(color: theme.background, borderRadius: BorderRadius.circular(25), border: Border.all(color: theme.playerBlue.withOpacity(0.5), width: 2), boxShadow: [BoxShadow(color: theme.playerBlue.withOpacity(0.3), blurRadius: 20, spreadRadius: 5)]),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 30.0, horizontal: 25.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(loc.welcomeTitle, style: getSharedTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.w900, fontSize: 24, letterSpacing: 1.5)), textAlign: TextAlign.center),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Text('Scegli il tuo nome da battaglia per sfidare i tuoi amici online.', style: getSharedTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.8), fontSize: 16)), textAlign: TextAlign.center),
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
TextField(
|
|
||||||
controller: nameController, textCapitalization: TextCapitalization.characters, textAlign: TextAlign.center, maxLength: 5,
|
|
||||||
style: getSharedTextStyle(themeType, TextStyle(color: theme.text, fontSize: 32, fontWeight: FontWeight.bold, letterSpacing: 8)),
|
|
||||||
decoration: InputDecoration(hintText: loc.nameHint, hintStyle: getSharedTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.3), letterSpacing: 4)), filled: true, fillColor: theme.text.withOpacity(0.05), counterText: "", enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: theme.gridLine.withOpacity(0.5), width: 2), borderRadius: BorderRadius.circular(15)), focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: theme.playerBlue, width: 3), borderRadius: BorderRadius.circular(15))),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity, height: 55,
|
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: theme.playerBlue, foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))),
|
|
||||||
onPressed: () {
|
|
||||||
final name = nameController.text.trim();
|
|
||||||
if (name.isNotEmpty) { StorageService.instance.savePlayerName(name); Navigator.of(context).pop(); setState(() {}); }
|
|
||||||
},
|
|
||||||
child: Text(loc.saveAndPlay, style: getSharedTextStyle(themeType, const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, letterSpacing: 1.5))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) dialogContent = AnimatedCyberBorder(child: dialogContent);
|
Future<void> handleAuth(bool isLogin) async {
|
||||||
return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.all(20), child: dialogContent);
|
final name = nameController.text.trim();
|
||||||
|
final password = passController.text.trim();
|
||||||
|
|
||||||
|
setStateDialog(() {
|
||||||
|
_errorMessage = "";
|
||||||
|
isLoadingAuth = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (name.isEmpty || password.isEmpty) {
|
||||||
|
setStateDialog(() {
|
||||||
|
_errorMessage = "Inserisci Nome e Password!";
|
||||||
|
isLoadingAuth = false;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (password.length < 6) {
|
||||||
|
setStateDialog(() {
|
||||||
|
_errorMessage = "La password deve avere almeno 6 caratteri!";
|
||||||
|
isLoadingAuth = false;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final fakeEmail = "${name.toLowerCase().replaceAll(' ', '')}@tetraq.game";
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isLogin) {
|
||||||
|
await FirebaseAuth.instance.signInWithEmailAndPassword(email: fakeEmail, password: password);
|
||||||
|
final doc = await FirebaseFirestore.instance.collection('leaderboard').doc(FirebaseAuth.instance.currentUser!.uid).get();
|
||||||
|
if (doc.exists) {
|
||||||
|
final data = doc.data() as Map<String, dynamic>;
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setInt('totalXP', data['xp'] ?? 0);
|
||||||
|
await prefs.setInt('wins', data['wins'] ?? 0);
|
||||||
|
await prefs.setInt('losses', data['losses'] ?? 0);
|
||||||
|
}
|
||||||
|
if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Bentornato $name! Dati sincronizzati."), backgroundColor: Colors.green));
|
||||||
|
} else {
|
||||||
|
await FirebaseAuth.instance.createUserWithEmailAndPassword(email: fakeEmail, password: password);
|
||||||
|
if (mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Account creato con successo!"), backgroundColor: Colors.green));
|
||||||
|
}
|
||||||
|
|
||||||
|
await StorageService.instance.savePlayerName(name);
|
||||||
|
if (mounted) Navigator.of(context).pop();
|
||||||
|
setState(() {});
|
||||||
|
|
||||||
|
} on FirebaseAuthException catch (e) {
|
||||||
|
String msg = "Errore di autenticazione.";
|
||||||
|
if (e.code == 'email-already-in-use') {
|
||||||
|
msg = "Nome occupato!\nSe è il tuo account, clicca su ACCEDI.";
|
||||||
|
} else if (e.code == 'user-not-found' || e.code == 'wrong-password' || e.code == 'invalid-credential') {
|
||||||
|
msg = "Nome o Password errati!";
|
||||||
|
}
|
||||||
|
setStateDialog(() {
|
||||||
|
_errorMessage = msg;
|
||||||
|
isLoadingAuth = false;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
setStateDialog(() {
|
||||||
|
_errorMessage = "Errore imprevisto: $e";
|
||||||
|
isLoadingAuth = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget dialogContent = themeType == AppThemeType.doodle
|
||||||
|
? CustomPaint(
|
||||||
|
painter: DoodleBackgroundPainter(fillColor: Colors.yellow.shade100, strokeColor: inkColor, seed: 100),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 20.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(loc.welcomeTitle, style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontWeight: FontWeight.w900, fontSize: 24, letterSpacing: 2.0)), textAlign: TextAlign.center),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text('Scegli Nome e Password.\nTi serviranno per recuperare gli XP!', style: getSharedTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.8), fontSize: 13)), textAlign: TextAlign.center),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
TextField(
|
||||||
|
controller: nameController, textCapitalization: TextCapitalization.characters, textAlign: TextAlign.center, maxLength: 8,
|
||||||
|
style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: 24, fontWeight: FontWeight.bold, letterSpacing: 4)),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: loc.nameHint,
|
||||||
|
hintStyle: getSharedTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.3), letterSpacing: 4)),
|
||||||
|
filled: false, counterText: "",
|
||||||
|
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: inkColor, width: 3)),
|
||||||
|
focusedBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.red.shade200, width: 5))
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: passController, obscureText: _obscurePassword, textAlign: TextAlign.center, maxLength: 20,
|
||||||
|
style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: 20, fontWeight: FontWeight.bold, letterSpacing: 8)),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: "PASSWORD",
|
||||||
|
hintStyle: getSharedTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.3), letterSpacing: 4)),
|
||||||
|
filled: false, counterText: "",
|
||||||
|
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: inkColor, width: 3)),
|
||||||
|
focusedBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.red.shade200, width: 5)),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(_obscurePassword ? Icons.visibility : Icons.visibility_off, color: inkColor.withOpacity(0.6)),
|
||||||
|
onPressed: () { setStateDialog(() { _obscurePassword = !_obscurePassword; }); },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
|
// MESSAGGIO DI ERRORE
|
||||||
|
if (_errorMessage.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10),
|
||||||
|
child: Text(_errorMessage, style: getSharedTextStyle(themeType, const TextStyle(color: Colors.red, fontSize: 14, fontWeight: FontWeight.bold)), textAlign: TextAlign.center),
|
||||||
|
),
|
||||||
|
|
||||||
|
Text("💡 Nota: Non serve una vera email. Usa una password facile da ricordare!", style: getSharedTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.6), fontSize: 11, height: 1.3)), textAlign: TextAlign.center),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
isLoadingAuth
|
||||||
|
? CircularProgressIndicator(color: inkColor)
|
||||||
|
: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => handleAuth(true),
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: DoodleBackgroundPainter(fillColor: Colors.blue.shade200, strokeColor: inkColor, seed: 101),
|
||||||
|
child: Container(height: 45, alignment: Alignment.center, child: Text("ACCEDI", style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: 14, fontWeight: FontWeight.bold, letterSpacing: 1.5)))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => handleAuth(false),
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: DoodleBackgroundPainter(fillColor: Colors.green.shade200, strokeColor: inkColor, seed: 102),
|
||||||
|
child: Container(height: 45, alignment: Alignment.center, child: Text("REGISTRATI", style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: 14, fontWeight: FontWeight.bold, letterSpacing: 1.5)))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(
|
||||||
|
decoration: BoxDecoration(color: theme.background, borderRadius: BorderRadius.circular(25), border: Border.all(color: theme.playerBlue.withOpacity(0.5), width: 2), boxShadow: [BoxShadow(color: theme.playerBlue.withOpacity(0.3), blurRadius: 20, spreadRadius: 5)]),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 20.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(loc.welcomeTitle, style: getSharedTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.w900, fontSize: 20, letterSpacing: 1.5)), textAlign: TextAlign.center),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text('Scegli Nome e Password.\nTi serviranno per recuperare gli XP!', style: getSharedTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.8), fontSize: 13)), textAlign: TextAlign.center),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
TextField(
|
||||||
|
controller: nameController, textCapitalization: TextCapitalization.characters, textAlign: TextAlign.center, maxLength: 8,
|
||||||
|
style: getSharedTextStyle(themeType, TextStyle(color: theme.text, fontSize: 24, fontWeight: FontWeight.bold, letterSpacing: 4)),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: loc.nameHint,
|
||||||
|
hintStyle: getSharedTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.3), letterSpacing: 4)),
|
||||||
|
filled: true, fillColor: theme.text.withOpacity(0.05), counterText: "",
|
||||||
|
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: theme.gridLine.withOpacity(0.5), width: 2), borderRadius: BorderRadius.circular(15)),
|
||||||
|
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: theme.playerBlue, width: 3), borderRadius: BorderRadius.circular(15))
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
TextField(
|
||||||
|
controller: passController, obscureText: _obscurePassword, textAlign: TextAlign.center, maxLength: 20,
|
||||||
|
style: getSharedTextStyle(themeType, TextStyle(color: theme.text, fontSize: 20, fontWeight: FontWeight.bold, letterSpacing: 8)),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: "PASSWORD",
|
||||||
|
hintStyle: getSharedTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.3), letterSpacing: 4)),
|
||||||
|
filled: true, fillColor: theme.text.withOpacity(0.05), counterText: "",
|
||||||
|
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: theme.gridLine.withOpacity(0.5), width: 2), borderRadius: BorderRadius.circular(15)),
|
||||||
|
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: theme.playerBlue, width: 3), borderRadius: BorderRadius.circular(15)),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(_obscurePassword ? Icons.visibility : Icons.visibility_off, color: theme.text.withOpacity(0.6)),
|
||||||
|
onPressed: () { setStateDialog(() { _obscurePassword = !_obscurePassword; }); },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
|
// MESSAGGIO DI ERRORE
|
||||||
|
if (_errorMessage.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10),
|
||||||
|
child: Text(_errorMessage, style: getSharedTextStyle(themeType, const TextStyle(color: Colors.redAccent, fontSize: 14, fontWeight: FontWeight.bold)), textAlign: TextAlign.center),
|
||||||
|
),
|
||||||
|
|
||||||
|
Text("💡 Nota: Non serve una vera email. Usa una password facile da ricordare!", style: getSharedTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.6), fontSize: 11, height: 1.3)), textAlign: TextAlign.center),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
isLoadingAuth
|
||||||
|
? CircularProgressIndicator(color: theme.playerBlue)
|
||||||
|
: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 45,
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(backgroundColor: theme.text.withOpacity(0.1), foregroundColor: theme.text, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), side: BorderSide(color: theme.playerBlue, width: 1.5)),
|
||||||
|
onPressed: () => handleAuth(true),
|
||||||
|
child: Text("ACCEDI", style: getSharedTextStyle(themeType, const TextStyle(fontSize: 13, fontWeight: FontWeight.bold, letterSpacing: 1.0))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 45,
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(backgroundColor: theme.playerBlue, foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))),
|
||||||
|
onPressed: () => handleAuth(false),
|
||||||
|
child: Text("REGISTRATI", style: getSharedTextStyle(themeType, const TextStyle(fontSize: 13, fontWeight: FontWeight.bold, letterSpacing: 1.0))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) dialogContent = AnimatedCyberBorder(child: dialogContent);
|
||||||
|
return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.all(20), child: dialogContent);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -765,11 +627,11 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 12, runSpacing: 12, alignment: WrapAlignment.center,
|
spacing: 12, runSpacing: 12, alignment: WrapAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
_NeonShapeButton(icon: Icons.diamond_outlined, label: 'Rombo', isSelected: localShape == ArenaShape.classic, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.classic)),
|
NeonShapeButton(icon: Icons.diamond_outlined, label: 'Rombo', isSelected: localShape == ArenaShape.classic, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.classic)),
|
||||||
_NeonShapeButton(icon: Icons.add, label: 'Croce', isSelected: localShape == ArenaShape.cross, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.cross)),
|
NeonShapeButton(icon: Icons.add, label: 'Croce', isSelected: localShape == ArenaShape.cross, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.cross)),
|
||||||
_NeonShapeButton(icon: Icons.donut_large, label: 'Buco', isSelected: localShape == ArenaShape.donut, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.donut)),
|
NeonShapeButton(icon: Icons.donut_large, label: 'Buco', isSelected: localShape == ArenaShape.donut, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.donut)),
|
||||||
_NeonShapeButton(icon: Icons.hourglass_bottom, label: 'Clessidra', isSelected: localShape == ArenaShape.hourglass, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.hourglass)),
|
NeonShapeButton(icon: Icons.hourglass_bottom, label: 'Clessidra', isSelected: localShape == ArenaShape.hourglass, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.hourglass)),
|
||||||
_NeonShapeButton(icon: Icons.all_inclusive, label: 'Caos', isSelected: localShape == ArenaShape.chaos, theme: theme, themeType: themeType, isSpecial: true, isLocked: !isChaosUnlocked, onTap: () => setStateDialog(() => localShape = ArenaShape.chaos)),
|
NeonShapeButton(icon: Icons.all_inclusive, label: 'Caos', isSelected: localShape == ArenaShape.chaos, theme: theme, themeType: themeType, isSpecial: true, isLocked: !isChaosUnlocked, onTap: () => setStateDialog(() => localShape = ArenaShape.chaos)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 25), Divider(color: inkColor.withOpacity(0.3), thickness: 2.5), const SizedBox(height: 20),
|
const SizedBox(height: 25), Divider(color: inkColor.withOpacity(0.3), thickness: 2.5), const SizedBox(height: 20),
|
||||||
|
|
@ -778,17 +640,17 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
_NeonSizeButton(label: 'S', isSelected: localRadius == 3, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 3)),
|
NeonSizeButton(label: 'S', isSelected: localRadius == 3, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 3)),
|
||||||
_NeonSizeButton(label: 'M', isSelected: localRadius == 4, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 4)),
|
NeonSizeButton(label: 'M', isSelected: localRadius == 4, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 4)),
|
||||||
_NeonSizeButton(label: 'L', isSelected: localRadius == 5, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 5)),
|
NeonSizeButton(label: 'L', isSelected: localRadius == 5, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 5)),
|
||||||
_NeonSizeButton(label: 'MAX', isSelected: localRadius == 6, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 6)),
|
NeonSizeButton(label: 'MAX', isSelected: localRadius == 6, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 6)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 25), Divider(color: inkColor.withOpacity(0.3), thickness: 2.5), const SizedBox(height: 20),
|
const SizedBox(height: 25), Divider(color: inkColor.withOpacity(0.3), thickness: 2.5), const SizedBox(height: 20),
|
||||||
],
|
],
|
||||||
|
|
||||||
Text("TEMPO", style: getSharedTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: inkColor.withOpacity(0.6), letterSpacing: 1.5))), const SizedBox(height: 10),
|
Text("TEMPO", style: getSharedTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: inkColor.withOpacity(0.6), letterSpacing: 1.5))), const SizedBox(height: 10),
|
||||||
_NeonTimeSwitch(isTimeMode: localTimeMode, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localTimeMode = !localTimeMode)), const SizedBox(height: 35),
|
NeonTimeSwitch(isTimeMode: localTimeMode, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localTimeMode = !localTimeMode)), const SizedBox(height: 35),
|
||||||
|
|
||||||
Transform.rotate(
|
Transform.rotate(
|
||||||
angle: -0.02,
|
angle: -0.02,
|
||||||
|
|
@ -829,11 +691,11 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 10, runSpacing: 10, alignment: WrapAlignment.center,
|
spacing: 10, runSpacing: 10, alignment: WrapAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
_NeonShapeButton(icon: Icons.diamond_outlined, label: 'Rombo', isSelected: localShape == ArenaShape.classic, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.classic)),
|
NeonShapeButton(icon: Icons.diamond_outlined, label: 'Rombo', isSelected: localShape == ArenaShape.classic, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.classic)),
|
||||||
_NeonShapeButton(icon: Icons.add, label: 'Croce', isSelected: localShape == ArenaShape.cross, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.cross)),
|
NeonShapeButton(icon: Icons.add, label: 'Croce', isSelected: localShape == ArenaShape.cross, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.cross)),
|
||||||
_NeonShapeButton(icon: Icons.donut_large, label: 'Buco', isSelected: localShape == ArenaShape.donut, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.donut)),
|
NeonShapeButton(icon: Icons.donut_large, label: 'Buco', isSelected: localShape == ArenaShape.donut, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.donut)),
|
||||||
_NeonShapeButton(icon: Icons.hourglass_bottom, label: 'Clessidra', isSelected: localShape == ArenaShape.hourglass, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.hourglass)),
|
NeonShapeButton(icon: Icons.hourglass_bottom, label: 'Clessidra', isSelected: localShape == ArenaShape.hourglass, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localShape = ArenaShape.hourglass)),
|
||||||
_NeonShapeButton(icon: Icons.all_inclusive, label: 'Caos', isSelected: localShape == ArenaShape.chaos, theme: theme, themeType: themeType, isSpecial: true, isLocked: !isChaosUnlocked, onTap: () => setStateDialog(() => localShape = ArenaShape.chaos)),
|
NeonShapeButton(icon: Icons.all_inclusive, label: 'Caos', isSelected: localShape == ArenaShape.chaos, theme: theme, themeType: themeType, isSpecial: true, isLocked: !isChaosUnlocked, onTap: () => setStateDialog(() => localShape = ArenaShape.chaos)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20), Divider(color: Colors.white.withOpacity(0.05), thickness: 2), const SizedBox(height: 20),
|
const SizedBox(height: 20), Divider(color: Colors.white.withOpacity(0.05), thickness: 2), const SizedBox(height: 20),
|
||||||
|
|
@ -842,17 +704,17 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
_NeonSizeButton(label: 'S', isSelected: localRadius == 3, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 3)),
|
NeonSizeButton(label: 'S', isSelected: localRadius == 3, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 3)),
|
||||||
_NeonSizeButton(label: 'M', isSelected: localRadius == 4, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 4)),
|
NeonSizeButton(label: 'M', isSelected: localRadius == 4, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 4)),
|
||||||
_NeonSizeButton(label: 'L', isSelected: localRadius == 5, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 5)),
|
NeonSizeButton(label: 'L', isSelected: localRadius == 5, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 5)),
|
||||||
_NeonSizeButton(label: 'MAX', isSelected: localRadius == 6, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 6)),
|
NeonSizeButton(label: 'MAX', isSelected: localRadius == 6, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localRadius = 6)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20), Divider(color: Colors.white.withOpacity(0.05), thickness: 2), const SizedBox(height: 20),
|
const SizedBox(height: 20), Divider(color: Colors.white.withOpacity(0.05), thickness: 2), const SizedBox(height: 20),
|
||||||
],
|
],
|
||||||
|
|
||||||
Text("TEMPO", style: getSharedTextStyle(themeType, TextStyle(fontSize: 12, fontWeight: FontWeight.w900, color: theme.text.withOpacity(0.5), letterSpacing: 1.5))), const SizedBox(height: 10),
|
Text("TEMPO", style: getSharedTextStyle(themeType, TextStyle(fontSize: 12, fontWeight: FontWeight.w900, color: theme.text.withOpacity(0.5), letterSpacing: 1.5))), const SizedBox(height: 10),
|
||||||
_NeonTimeSwitch(isTimeMode: localTimeMode, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localTimeMode = !localTimeMode)), const SizedBox(height: 30),
|
NeonTimeSwitch(isTimeMode: localTimeMode, theme: theme, themeType: themeType, onTap: () => setStateDialog(() => localTimeMode = !localTimeMode)), const SizedBox(height: 30),
|
||||||
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity, height: 60,
|
width: double.infinity, height: 60,
|
||||||
|
|
@ -905,6 +767,8 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_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.cyberpunk) bgImage = 'assets/images/cyber_bg.jpg';
|
||||||
if (themeType == AppThemeType.music) bgImage = 'assets/images/music_bg.jpg';
|
if (themeType == AppThemeType.music) bgImage = 'assets/images/music_bg.jpg';
|
||||||
|
if (themeType == AppThemeType.arcade) bgImage = 'assets/images/arcade.jpg';
|
||||||
|
if (themeType == AppThemeType.grimorio) bgImage = 'assets/images/grimorio.jpg'; // Aggiunto Grimorio
|
||||||
|
|
||||||
int wins = StorageService.instance.wins;
|
int wins = StorageService.instance.wins;
|
||||||
int losses = StorageService.instance.losses;
|
int losses = StorageService.instance.losses;
|
||||||
|
|
@ -933,8 +797,8 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
// BLOCCO SINISTRO: AVATAR, NOME E AUDIO
|
// BLOCCO SINISTRO: AVATAR E NOME
|
||||||
Expanded( // Permette di occupare lo spazio necessario senza spingere la destra
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -979,7 +843,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// BLOCCO DESTRO: STATISTICHE (SOPRA) E AUDIO (SOTTO)
|
// BLOCCO DESTRO: STATISTICHE E AUDIO
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -1175,7 +1039,9 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
);
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: bgImage != null ? Colors.transparent : theme.background,
|
||||||
|
extendBodyBehindAppBar: true,
|
||||||
|
appBar: AppBar(backgroundColor: Colors.transparent, elevation: 0, iconTheme: IconThemeData(color: theme.text)),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
// 1. Sfondo base a tinta unita
|
// 1. Sfondo base a tinta unita
|
||||||
|
|
@ -1199,8 +1065,8 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// 4. Patina scura (Cyberpunk e Music)
|
// 4. Patina scura (Cyberpunk, Music, Arcade e Grimorio)
|
||||||
if (bgImage != null && (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music))
|
if (bgImage != null && (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music || themeType == AppThemeType.arcade || themeType == AppThemeType.grimorio))
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,74 @@
|
||||||
|
// ===========================================================================
|
||||||
|
// FILE: lib/widgets/custom_settings_button.dart
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../theme/app_colors.dart';
|
import '../core/app_colors.dart';
|
||||||
|
import 'painters.dart'; // Importiamo i painter per i doodle e il font
|
||||||
|
|
||||||
// Widget per i pulsanti di selezione della forma dell'arena
|
|
||||||
class NeonShapeButton extends StatelessWidget {
|
class NeonShapeButton extends StatelessWidget {
|
||||||
final IconData icon;
|
final IconData icon; final String label; final bool isSelected;
|
||||||
final String label;
|
final ThemeColors theme; final AppThemeType themeType; final VoidCallback onTap;
|
||||||
final bool isSelected;
|
final bool isLocked; final bool isSpecial;
|
||||||
final VoidCallback onTap;
|
|
||||||
final ShapeBorder shape; // La forma geometrica del pulsante
|
|
||||||
|
|
||||||
const NeonShapeButton({
|
const NeonShapeButton({super.key, required this.icon, required this.label, required this.isSelected, required this.theme, required this.themeType, required this.onTap, this.isLocked = false, this.isSpecial = false});
|
||||||
super.key,
|
|
||||||
required this.icon,
|
Color _getDoodleColor() {
|
||||||
required this.label,
|
switch (label) {
|
||||||
required this.isSelected,
|
case 'Rombo': return Colors.lightBlue.shade200;
|
||||||
required this.onTap,
|
case 'Croce': return Colors.green.shade200;
|
||||||
this.shape = const RoundedRectangleBorder( // Forma di default
|
case 'Buco': return Colors.pink.shade200;
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12.0))),
|
case 'Clessidra': return Colors.purple.shade200;
|
||||||
});
|
case 'Caos': return Colors.grey.shade300;
|
||||||
|
default: return Colors.lightBlue.shade200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
if (themeType == AppThemeType.doodle) {
|
||||||
onTap: onTap,
|
Color doodleColor = isLocked ? Colors.grey : _getDoodleColor();
|
||||||
child: AnimatedContainer(
|
Color inkColor = const Color(0xFF111122);
|
||||||
duration: const Duration(milliseconds: 300),
|
double tilt = (label.length % 2 == 0) ? -0.05 : 0.04;
|
||||||
curve: Curves.easeInOut,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
return Transform.rotate(
|
||||||
decoration: ShapeDecoration(
|
angle: tilt,
|
||||||
shape: shape,
|
child: GestureDetector(
|
||||||
color: isSelected
|
onTap: isLocked ? null : onTap,
|
||||||
? AppColors.neonGreen.withOpacity(0.2) // Sfondo luminoso se selezionato
|
child: CustomPaint(
|
||||||
: AppColors.surface.withOpacity(0.5), // Sfondo più scuro se non selezionato
|
painter: DoodleBackgroundPainter(fillColor: isSelected ? doodleColor : Colors.white.withOpacity(0.8), strokeColor: inkColor, seed: label.length * 3),
|
||||||
shadows: isSelected
|
child: Container(
|
||||||
? [ // Bagliore intenso se selezionato
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||||
BoxShadow(
|
child: Column(
|
||||||
color: AppColors.neonGreen.withOpacity(0.6),
|
mainAxisSize: MainAxisSize.min,
|
||||||
blurRadius: 12.0,
|
children: [
|
||||||
spreadRadius: 2.0,
|
Icon(isLocked ? Icons.lock : icon, color: inkColor, size: 24),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(isLocked ? "Liv. 10" : label, style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: 11, fontWeight: FontWeight.w900, letterSpacing: 0.5))),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
),
|
||||||
: [],
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color mainColor = isSpecial && !isLocked ? Colors.purpleAccent : theme.playerBlue;
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: isLocked ? null : onTap,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 250), curve: Curves.easeOutCubic, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), transform: Matrix4.translationValues(0, isSelected ? 2 : 0, 0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(15), border: Border.all(color: isLocked ? Colors.transparent : (isSelected ? mainColor : Colors.white.withOpacity(0.1)), width: isSelected ? 2 : 1),
|
||||||
|
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isLocked ? [Colors.grey.withOpacity(0.1), Colors.black.withOpacity(0.2)] : isSelected ? [mainColor.withOpacity(0.3), mainColor.withOpacity(0.1)] : [theme.text.withOpacity(0.1), theme.text.withOpacity(0.02)]),
|
||||||
|
boxShadow: isLocked ? [] : isSelected ? [BoxShadow(color: mainColor.withOpacity(0.5), blurRadius: 15, spreadRadius: 1, offset: const Offset(0, 0))] : [BoxShadow(color: Colors.black.withOpacity(0.4), blurRadius: 6, offset: const Offset(2, 4)), BoxShadow(color: Colors.white.withOpacity(0.05), blurRadius: 2, offset: const Offset(-1, -1))],
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(isLocked ? Icons.lock : icon, color: isLocked ? Colors.grey.withOpacity(0.5) : (isSelected ? Colors.white : theme.text.withOpacity(0.6)), size: 24),
|
||||||
icon,
|
const SizedBox(height: 6),
|
||||||
color: isSelected ? AppColors.neonGreen : AppColors.textSecondary,
|
Text(isLocked ? "Liv. 10" : label, style: getSharedTextStyle(themeType, TextStyle(color: isLocked ? Colors.grey.withOpacity(0.5) : (isSelected ? Colors.white : theme.text.withOpacity(0.6)), fontSize: 11, fontWeight: isSelected ? FontWeight.w900 : FontWeight.bold))),
|
||||||
size: 28,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
color: isSelected ? AppColors.textPrimary : AppColors.textSecondary,
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -66,114 +76,169 @@ class NeonShapeButton extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget per i pulsanti di selezione della taglia dell'arena
|
|
||||||
class NeonSizeButton extends StatelessWidget {
|
class NeonSizeButton extends StatelessWidget {
|
||||||
final String label;
|
final String label; final bool isSelected; final ThemeColors theme; final AppThemeType themeType; final VoidCallback onTap;
|
||||||
final bool isSelected;
|
const NeonSizeButton({super.key, required this.label, required this.isSelected, required this.theme, required this.themeType, required this.onTap});
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
const NeonSizeButton({
|
|
||||||
super.key,
|
|
||||||
required this.label,
|
|
||||||
required this.isSelected,
|
|
||||||
required this.onTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (themeType == AppThemeType.doodle) {
|
||||||
|
Color doodleColor = label == 'MAX' ? Colors.red.shade200 : Colors.cyan.shade100; Color inkColor = const Color(0xFF111122); double tilt = (label == 'M' || label == 'MAX') ? 0.05 : -0.04;
|
||||||
|
return Transform.rotate(
|
||||||
|
angle: tilt,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: CustomPaint(painter: DoodleBackgroundPainter(fillColor: isSelected ? doodleColor : Colors.white.withOpacity(0.8), strokeColor: inkColor, seed: label.codeUnitAt(0), isCircle: true), child: SizedBox(width: 50, height: 50, child: Center(child: Text(label, style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: 18, fontWeight: FontWeight.w900)))))),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 250), curve: Curves.easeOutCubic, width: 50, height: 50, transform: Matrix4.translationValues(0, isSelected ? 2 : 0, 0),
|
||||||
curve: Curves.easeInOut,
|
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle, // Forma circolare
|
shape: BoxShape.circle, border: Border.all(color: isSelected ? theme.playerRed : Colors.white.withOpacity(0.1), width: isSelected ? 2 : 1),
|
||||||
color: isSelected
|
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isSelected ? [theme.playerRed.withOpacity(0.3), theme.playerRed.withOpacity(0.1)] : [theme.text.withOpacity(0.1), theme.text.withOpacity(0.02)]),
|
||||||
? AppColors.neonBlue.withOpacity(0.2)
|
boxShadow: isSelected ? [BoxShadow(color: theme.playerRed.withOpacity(0.5), blurRadius: 15, spreadRadius: 1)] : [BoxShadow(color: Colors.black.withOpacity(0.4), blurRadius: 6, offset: const Offset(2, 4)), BoxShadow(color: Colors.white.withOpacity(0.05), blurRadius: 2, offset: const Offset(-1, -1))],
|
||||||
: AppColors.surface.withOpacity(0.5),
|
|
||||||
border: Border.all(
|
|
||||||
color: isSelected ? AppColors.neonBlue : AppColors.surfaceLight,
|
|
||||||
width: 2.0,
|
|
||||||
),
|
|
||||||
shadows: isSelected
|
|
||||||
? [
|
|
||||||
BoxShadow(
|
|
||||||
color: AppColors.neonBlue.withOpacity(0.6),
|
|
||||||
blurRadius: 10.0,
|
|
||||||
spreadRadius: 1.5,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: [],
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
color: isSelected ? AppColors.textPrimary : AppColors.textSecondary,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
child: Center(child: Text(label, style: getSharedTextStyle(themeType, TextStyle(color: isSelected ? Colors.white : theme.text.withOpacity(0.6), fontSize: 14, fontWeight: isSelected ? FontWeight.w900 : FontWeight.bold)))),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget per l'interruttore della modalità tempo (Clessidra)
|
|
||||||
class NeonTimeSwitch extends StatelessWidget {
|
class NeonTimeSwitch extends StatelessWidget {
|
||||||
final bool isTimeMode;
|
final bool isTimeMode; final ThemeColors theme; final AppThemeType themeType; final VoidCallback onTap;
|
||||||
final VoidCallback onTap;
|
const NeonTimeSwitch({super.key, required this.isTimeMode, required this.theme, required this.themeType, required this.onTap});
|
||||||
|
|
||||||
const NeonTimeSwitch({
|
|
||||||
super.key,
|
|
||||||
required this.isTimeMode,
|
|
||||||
required this.onTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (themeType == AppThemeType.doodle) {
|
||||||
|
Color doodleColor = Colors.orange.shade200; Color inkColor = const Color(0xFF111122);
|
||||||
|
return Transform.rotate(
|
||||||
|
angle: -0.015,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: DoodleBackgroundPainter(fillColor: isTimeMode ? doodleColor : Colors.white.withOpacity(0.8), strokeColor: inkColor, seed: 42),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(isTimeMode ? Icons.timer : Icons.timer_off, color: inkColor, size: 28), const SizedBox(width: 12),
|
||||||
|
Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [Text(isTimeMode ? 'A TEMPO' : 'RELAX', style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontWeight: FontWeight.w900, fontSize: 16, letterSpacing: 2.0))), Text(isTimeMode ? '15 sec a mossa' : 'Nessun limite', style: getSharedTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.8), fontSize: 13, fontWeight: FontWeight.bold)))]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
curve: Curves.easeInOut,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(30.0), // Forma arrotondata per lo switch
|
borderRadius: BorderRadius.circular(20), border: Border.all(color: isTimeMode ? Colors.amber : Colors.white.withOpacity(0.1), width: isTimeMode ? 2 : 1),
|
||||||
color: isTimeMode
|
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isTimeMode ? [Colors.amber.withOpacity(0.25), Colors.amber.withOpacity(0.05)] : [theme.text.withOpacity(0.1), theme.text.withOpacity(0.02)]),
|
||||||
? AppColors.neonGreen.withOpacity(0.2)
|
boxShadow: isTimeMode ? [BoxShadow(color: Colors.amber.withOpacity(0.3), blurRadius: 15, spreadRadius: 2)] : [BoxShadow(color: Colors.black.withOpacity(0.4), blurRadius: 6, offset: const Offset(2, 4)), BoxShadow(color: Colors.white.withOpacity(0.05), blurRadius: 2, offset: const Offset(-1, -1))],
|
||||||
: AppColors.surface.withOpacity(0.5),
|
|
||||||
border: Border.all(
|
|
||||||
color: isTimeMode ? AppColors.neonGreen : AppColors.surfaceLight,
|
|
||||||
width: 2.0,
|
|
||||||
),
|
|
||||||
shadows: isTimeMode
|
|
||||||
? [
|
|
||||||
BoxShadow(
|
|
||||||
color: AppColors.neonGreen.withOpacity(0.6),
|
|
||||||
blurRadius: 12.0,
|
|
||||||
spreadRadius: 2.0,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: [],
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(isTimeMode ? Icons.timer : Icons.timer_off, color: isTimeMode ? Colors.amber : theme.text.withOpacity(0.5), size: 28), const SizedBox(width: 12),
|
||||||
Icons.hourglass_empty, // Icona clessidra
|
Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [Text(isTimeMode ? 'A TEMPO' : 'RELAX', style: getSharedTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.white : theme.text.withOpacity(0.5), fontWeight: FontWeight.w900, fontSize: 14, letterSpacing: 1.5))), Text(isTimeMode ? '15 sec a mossa' : 'Nessun limite', style: getSharedTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.amber.shade200 : theme.text.withOpacity(0.4), fontSize: 11, fontWeight: FontWeight.bold)))]),
|
||||||
color: isTimeMode ? AppColors.neonGreen : AppColors.textSecondary,
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
),
|
||||||
Text(
|
);
|
||||||
isTimeMode ? 'A TEMPO' : 'SENZA TEMPO',
|
}
|
||||||
style: TextStyle(
|
}
|
||||||
color: isTimeMode ? AppColors.textPrimary : AppColors.textSecondary,
|
|
||||||
fontWeight: isTimeMode ? FontWeight.bold : FontWeight.normal,
|
class NeonPrivacySwitch extends StatelessWidget {
|
||||||
|
final bool isPublic;
|
||||||
|
final ThemeColors theme;
|
||||||
|
final AppThemeType themeType;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const NeonPrivacySwitch({super.key, required this.isPublic, required this.theme, required this.themeType, required this.onTap});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (themeType == AppThemeType.doodle) {
|
||||||
|
Color doodleColor = isPublic ? Colors.green.shade600 : Colors.red.shade600;
|
||||||
|
return Transform.rotate(
|
||||||
|
angle: 0.015,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
transform: Matrix4.translationValues(0, isPublic ? 3 : 0, 0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isPublic ? doodleColor : Colors.white,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(15), topRight: Radius.circular(8),
|
||||||
|
bottomLeft: Radius.circular(6), bottomRight: Radius.circular(15),
|
||||||
),
|
),
|
||||||
|
border: Border.all(color: isPublic ? theme.text : doodleColor.withOpacity(0.5), width: 2.5),
|
||||||
|
boxShadow: [BoxShadow(color: isPublic ? theme.text.withOpacity(0.8) : doodleColor.withOpacity(0.2), offset: const Offset(4, 5), blurRadius: 0)],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(isPublic ? Icons.public : Icons.lock, color: isPublic ? Colors.white : doodleColor, size: 20),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(isPublic ? 'PUBBLICA' : 'PRIVATA', style: getSharedTextStyle(themeType, TextStyle(color: isPublic ? Colors.white : doodleColor, fontWeight: FontWeight.w900, fontSize: 12, letterSpacing: 1.0))),
|
||||||
|
Text(isPublic ? 'In Bacheca' : 'Solo Codice', style: getSharedTextStyle(themeType, TextStyle(color: isPublic ? Colors.white : doodleColor.withOpacity(0.8), fontSize: 9, fontWeight: FontWeight.bold))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: isPublic
|
||||||
|
? [Colors.greenAccent.withOpacity(0.25), Colors.greenAccent.withOpacity(0.05)]
|
||||||
|
: [theme.playerRed.withOpacity(0.25), theme.playerRed.withOpacity(0.05)],
|
||||||
|
),
|
||||||
|
border: Border.all(color: isPublic ? Colors.greenAccent : theme.playerRed, width: isPublic ? 2 : 1),
|
||||||
|
boxShadow: isPublic
|
||||||
|
? [BoxShadow(color: Colors.greenAccent.withOpacity(0.3), blurRadius: 15, spreadRadius: 2)]
|
||||||
|
: [BoxShadow(color: Colors.black.withOpacity(0.4), blurRadius: 6, offset: const Offset(2, 4))],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(isPublic ? Icons.public : Icons.lock, color: isPublic ? Colors.greenAccent : theme.playerRed, size: 20),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(isPublic ? 'PUBBLICA' : 'PRIVATA', style: getSharedTextStyle(themeType, TextStyle(color: isPublic ? Colors.white : theme.text.withOpacity(0.8), fontWeight: FontWeight.w900, fontSize: 11, letterSpacing: 1.5))),
|
||||||
|
Text(isPublic ? 'Tutti ti vedono' : 'Solo con Codice', style: getSharedTextStyle(themeType, TextStyle(color: isPublic ? Colors.greenAccent.shade200 : theme.playerRed.withOpacity(0.7), fontSize: 9, fontWeight: FontWeight.bold))),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -181,3 +246,72 @@ class NeonTimeSwitch extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NeonActionButton extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final Color color;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
final ThemeColors theme;
|
||||||
|
final AppThemeType themeType;
|
||||||
|
|
||||||
|
const NeonActionButton({super.key, required this.label, required this.color, required this.onTap, required this.theme, required this.themeType});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (themeType == AppThemeType.doodle) {
|
||||||
|
double tilt = (label == "UNISCITI" || label == "ANNULLA") ? -0.015 : 0.02;
|
||||||
|
return Transform.rotate(
|
||||||
|
angle: tilt,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
height: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10), topRight: Radius.circular(20),
|
||||||
|
bottomLeft: Radius.circular(25), bottomRight: Radius.circular(10),
|
||||||
|
),
|
||||||
|
border: Border.all(color: theme.text, width: 3.0),
|
||||||
|
boxShadow: [BoxShadow(color: theme.text.withOpacity(0.9), offset: const Offset(4, 4), blurRadius: 0)],
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
|
child: Text(label, style: getSharedTextStyle(themeType, TextStyle(fontSize: 20, fontWeight: FontWeight.w900, letterSpacing: 3.0, color: Colors.white))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
height: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [color.withOpacity(0.9), color.withOpacity(0.6)]),
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
border: Border.all(color: Colors.white.withOpacity(0.3), width: 1.5),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(color: Colors.black.withOpacity(0.5), offset: const Offset(4, 8), blurRadius: 12),
|
||||||
|
BoxShadow(color: color.withOpacity(0.3), offset: const Offset(0, 0), blurRadius: 15, spreadRadius: 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
|
child: Text(label, style: getSharedTextStyle(themeType, const TextStyle(fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 2.0, color: Colors.white, shadows: [Shadow(color: Colors.black, blurRadius: 2, offset: Offset(1, 1))]))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue