Auto-sync: 20260321_110000
This commit is contained in:
parent
5b99d5f0bb
commit
c990085df7
4 changed files with 37 additions and 41 deletions
|
|
@ -122,7 +122,7 @@ class QuestsDialog extends StatelessWidget {
|
||||||
// 2. DIALOGO CLASSIFICA (LEADERBOARD) CON CALLBACK SFIDA
|
// 2. DIALOGO CLASSIFICA (LEADERBOARD) CON CALLBACK SFIDA
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
class LeaderboardDialog extends StatelessWidget {
|
class LeaderboardDialog extends StatelessWidget {
|
||||||
final Function(String uid, String name)? onChallenge; // <-- Aggiunto Callback per inviare i dati alla HomeScreen
|
final Function(String uid, String name)? onChallenge;
|
||||||
|
|
||||||
const LeaderboardDialog({super.key, this.onChallenge});
|
const LeaderboardDialog({super.key, this.onChallenge});
|
||||||
|
|
||||||
|
|
@ -165,7 +165,8 @@ class LeaderboardDialog extends StatelessWidget {
|
||||||
final filteredDocs = rawDocs.where((doc) {
|
final filteredDocs = rawDocs.where((doc) {
|
||||||
var data = doc.data() as Map<String, dynamic>;
|
var data = doc.data() as Map<String, dynamic>;
|
||||||
String name = (data['name'] ?? '').toString().toUpperCase();
|
String name = (data['name'] ?? '').toString().toUpperCase();
|
||||||
return name != 'PAOLO';
|
// Nascondiamo PIPPO dalla classifica
|
||||||
|
return name != 'PIPPO';
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
if (filteredDocs.isEmpty) {
|
if (filteredDocs.isEmpty) {
|
||||||
|
|
@ -224,7 +225,6 @@ class LeaderboardDialog extends StatelessWidget {
|
||||||
themeType: themeType,
|
themeType: themeType,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
// Chiama la funzione passata dalla HomeScreen!
|
|
||||||
if (onChallenge != null) {
|
if (onChallenge != null) {
|
||||||
onChallenge!(doc.id, playerName);
|
onChallenge!(doc.id, playerName);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import '../../l10n/app_localizations.dart';
|
||||||
import '../../widgets/painters.dart';
|
import '../../widgets/painters.dart';
|
||||||
import '../../widgets/cyber_border.dart';
|
import '../../widgets/cyber_border.dart';
|
||||||
import '../game/game_screen.dart';
|
import '../game/game_screen.dart';
|
||||||
import '../multiplayer/lobby_screen.dart';
|
|
||||||
import '../multiplayer/lobby_widgets.dart';
|
import '../multiplayer/lobby_widgets.dart';
|
||||||
|
|
||||||
class HomeModals {
|
class HomeModals {
|
||||||
|
|
@ -62,25 +61,16 @@ class HomeModals {
|
||||||
final fakeEmail = "${name.toLowerCase().replaceAll(' ', '')}@tetraq.game";
|
final fakeEmail = "${name.toLowerCase().replaceAll(' ', '')}@tetraq.game";
|
||||||
final currentUser = FirebaseAuth.instance.currentUser;
|
final currentUser = FirebaseAuth.instance.currentUser;
|
||||||
|
|
||||||
// CATTURIAMO IL FANTASMA: Se siamo in un account anonimo provvisorio, ci salviamo il suo ID
|
|
||||||
final ghostUid = (currentUser != null && currentUser.isAnonymous) ? currentUser.uid : null;
|
final ghostUid = (currentUser != null && currentUser.isAnonymous) ? currentUser.uid : null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isLogin) {
|
if (isLogin) {
|
||||||
// ==========================================
|
|
||||||
// 1. TENTA IL LOGIN UFFICIALE
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
// --- LA MOSSA DEL NINJA: ELIMINIAMO IL GHOST PRIMA DEL LOGIN ---
|
|
||||||
// Lo eliminiamo ORA perché abbiamo ancora i permessi dell'utente anonimo.
|
|
||||||
if (ghostUid != null) {
|
if (ghostUid != null) {
|
||||||
await FirebaseFirestore.instance.collection('leaderboard').doc(ghostUid).delete().catchError((e) => null);
|
await FirebaseFirestore.instance.collection('leaderboard').doc(ghostUid).delete().catchError((e) => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ora effettuiamo l'accesso
|
|
||||||
await FirebaseAuth.instance.signInWithEmailAndPassword(email: fakeEmail, password: password);
|
await FirebaseAuth.instance.signInWithEmailAndPassword(email: fakeEmail, password: password);
|
||||||
|
|
||||||
// Recupera i dati storici dal Cloud per sovrascrivere quelli in locale
|
|
||||||
final doc = await FirebaseFirestore.instance.collection('leaderboard').doc(FirebaseAuth.instance.currentUser!.uid).get();
|
final doc = await FirebaseFirestore.instance.collection('leaderboard').doc(FirebaseAuth.instance.currentUser!.uid).get();
|
||||||
if (doc.exists) {
|
if (doc.exists) {
|
||||||
final data = doc.data() as Map<String, dynamic>;
|
final data = doc.data() as Map<String, dynamic>;
|
||||||
|
|
@ -95,22 +85,16 @@ class HomeModals {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// ==========================================
|
|
||||||
// 2. TENTA LA REGISTRAZIONE / UPGRADE
|
|
||||||
// ==========================================
|
|
||||||
if (currentUser != null && currentUser.isAnonymous) {
|
if (currentUser != null && currentUser.isAnonymous) {
|
||||||
// L'utente aveva un account anonimo, lo promuoviamo ad account con password
|
|
||||||
final credential = EmailAuthProvider.credential(email: fakeEmail, password: password);
|
final credential = EmailAuthProvider.credential(email: fakeEmail, password: password);
|
||||||
await currentUser.linkWithCredential(credential);
|
await currentUser.linkWithCredential(credential);
|
||||||
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Profilo Cloud protetto con successo!"), backgroundColor: Colors.green));
|
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Profilo Cloud protetto con successo!"), backgroundColor: Colors.green));
|
||||||
} else {
|
} else {
|
||||||
// Se per caso si trova senza nessun account, ne creiamo uno nuovo
|
|
||||||
await FirebaseAuth.instance.createUserWithEmailAndPassword(email: fakeEmail, password: password);
|
await FirebaseAuth.instance.createUserWithEmailAndPassword(email: fakeEmail, password: password);
|
||||||
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Account creato con successo!"), backgroundColor: Colors.green));
|
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Account creato con successo!"), backgroundColor: Colors.green));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Salva il nome e lancia il sync
|
|
||||||
await StorageService.instance.savePlayerName(name);
|
await StorageService.instance.savePlayerName(name);
|
||||||
StorageService.instance.syncLeaderboard();
|
StorageService.instance.syncLeaderboard();
|
||||||
|
|
||||||
|
|
@ -123,7 +107,6 @@ class HomeModals {
|
||||||
msg = "Nome già registrato!\nSe sei tu, clicca su ACCEDI.";
|
msg = "Nome già registrato!\nSe sei tu, clicca su ACCEDI.";
|
||||||
} else if (e.code == 'user-not-found' || e.code == 'wrong-password' || e.code == 'invalid-credential') {
|
} else if (e.code == 'user-not-found' || e.code == 'wrong-password' || e.code == 'invalid-credential') {
|
||||||
msg = "Nome o Password errati!";
|
msg = "Nome o Password errati!";
|
||||||
// Siccome avevamo cancellato il ghost prima, se il login fallisce lo ricreiamo per non perdere i dati locali
|
|
||||||
if (isLogin && ghostUid != null) StorageService.instance.syncLeaderboard();
|
if (isLogin && ghostUid != null) StorageService.instance.syncLeaderboard();
|
||||||
} else if (e.code == 'requires-recent-login') {
|
} else if (e.code == 'requires-recent-login') {
|
||||||
msg = "Errore di sessione. Riavvia l'app.";
|
msg = "Errore di sessione. Riavvia l'app.";
|
||||||
|
|
@ -534,7 +517,6 @@ class HomeModals {
|
||||||
),
|
),
|
||||||
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),
|
||||||
|
|
||||||
// TEMPO È ORA ESCLUSIVO PER IL MULTIPLAYER
|
|
||||||
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),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
_checkClipboardForInvite();
|
_checkClipboardForInvite();
|
||||||
_listenToFavoritesOnline();
|
_listenToFavoritesOnline();
|
||||||
} else if (state == AppLifecycleState.detached) {
|
} else if (state == AppLifecycleState.detached) {
|
||||||
// --- FIX BUG WHATSAPP: Rimossa l'eliminazione della stanza durante lo stato "paused" ---
|
|
||||||
_cleanupGhostRoom();
|
_cleanupGhostRoom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -581,8 +580,8 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
_debugTapCount++;
|
_debugTapCount++;
|
||||||
|
|
||||||
// CHEAT LOCALE VIVO SOLO IN DEBUG MODE
|
// CHEAT LOCALE VIVO SOLO IN DEBUG MODE (ORA CON PIPPO!)
|
||||||
if (kDebugMode && playerName.toUpperCase() == 'PAOLO' && _debugTapCount == 5) {
|
if (kDebugMode && playerName.toUpperCase() == 'PIPPO' && _debugTapCount == 5) {
|
||||||
StorageService.instance.addXP(2000);
|
StorageService.instance.addXP(2000);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
|
@ -593,7 +592,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
else if (_debugTapCount >= 7) {
|
else if (_debugTapCount >= 7) {
|
||||||
_debugTapCount = 0;
|
_debugTapCount = 0;
|
||||||
|
|
||||||
if (kDebugMode && playerName.toUpperCase() == 'PAOLO') {
|
if (kDebugMode && playerName.toUpperCase() == 'PIPPO') {
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (_) => const AdminScreen()));
|
Navigator.push(context, MaterialPageRoute(builder: (_) => const AdminScreen()));
|
||||||
} else {
|
} else {
|
||||||
bool isAdmin = await StorageService.instance.isUserAdmin();
|
bool isAdmin = await StorageService.instance.isUserAdmin();
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.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 'package:tetraq/l10n/app_localizations.dart'; // <-- IMPORT DEL DIZIONARIO!
|
import 'package:tetraq/l10n/app_localizations.dart';
|
||||||
|
|
||||||
import '../../logic/game_controller.dart';
|
import '../../logic/game_controller.dart';
|
||||||
import '../../models/game_board.dart';
|
import '../../models/game_board.dart';
|
||||||
|
|
@ -16,8 +16,8 @@ import '../../core/app_colors.dart';
|
||||||
import '../../services/multiplayer_service.dart';
|
import '../../services/multiplayer_service.dart';
|
||||||
import '../../services/storage_service.dart';
|
import '../../services/storage_service.dart';
|
||||||
import '../game/game_screen.dart';
|
import '../game/game_screen.dart';
|
||||||
import '../../widgets/painters.dart';
|
|
||||||
import '../../widgets/cyber_border.dart';
|
import '../../widgets/cyber_border.dart';
|
||||||
|
import '../../widgets/painters.dart'; // <--- ECCO L'IMPORT MANCANTE!
|
||||||
import 'lobby_widgets.dart';
|
import 'lobby_widgets.dart';
|
||||||
|
|
||||||
class LobbyScreen extends StatefulWidget {
|
class LobbyScreen extends StatefulWidget {
|
||||||
|
|
@ -45,7 +45,6 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
String _timeModeSetting = 'fixed';
|
String _timeModeSetting = 'fixed';
|
||||||
|
|
||||||
bool _isPublicRoom = true;
|
bool _isPublicRoom = true;
|
||||||
|
|
||||||
bool _roomStarted = false;
|
bool _roomStarted = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -220,17 +219,17 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (context) {
|
builder: (dialogContext) {
|
||||||
final theme = context.watch<ThemeManager>().currentColors;
|
final theme = dialogContext.watch<ThemeManager>().currentColors;
|
||||||
final themeType = context.read<ThemeManager>().currentThemeType;
|
final themeType = dialogContext.read<ThemeManager>().currentThemeType;
|
||||||
final loc = AppLocalizations.of(context)!;
|
final loc = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
Widget dialogContent = Column(
|
Widget dialogContent = Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
CircularProgressIndicator(color: theme.playerRed), const SizedBox(height: 25),
|
CircularProgressIndicator(color: theme.playerRed), const SizedBox(height: 25),
|
||||||
Text(loc.codeHint, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: theme.text.withOpacity(0.6), letterSpacing: 2))),
|
Text(loc.codeHint, style: getSharedTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: theme.text.withOpacity(0.6), letterSpacing: 2))),
|
||||||
Text(code, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 40, fontWeight: FontWeight.w900, color: theme.playerRed, letterSpacing: 8, shadows: themeType == AppThemeType.doodle ? [] : [Shadow(color: theme.playerRed.withOpacity(0.5), blurRadius: 10)]))),
|
Text(code, style: getSharedTextStyle(themeType, TextStyle(fontSize: 40, fontWeight: FontWeight.w900, color: theme.playerRed, letterSpacing: 8, shadows: themeType == AppThemeType.doodle ? [] : [Shadow(color: theme.playerRed.withOpacity(0.5), blurRadius: 10)]))),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
Transform.rotate(
|
Transform.rotate(
|
||||||
angle: themeType == AppThemeType.doodle ? 0.02 : 0,
|
angle: themeType == AppThemeType.doodle ? 0.02 : 0,
|
||||||
|
|
@ -247,9 +246,9 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Icon(_isPublicRoom ? Icons.podcasts : Icons.share, color: theme.playerBlue, size: 32), const SizedBox(height: 12),
|
Icon(_isPublicRoom ? Icons.podcasts : Icons.share, color: theme.playerBlue, size: 32), const SizedBox(height: 12),
|
||||||
Text(_isPublicRoom ? "Sei in Bacheca!" : "Condividi link", textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.w900, fontSize: 18))),
|
Text(_isPublicRoom ? "Sei in Bacheca!" : "Invito inviato", textAlign: TextAlign.center, style: getSharedTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.w900, fontSize: 18))),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(_isPublicRoom ? "Aspettiamo che uno sfidante si unisca dalla lobby pubblica." : "Condividi il codice. La partita inizierà appena si unirà.", textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.8), fontSize: 14, height: 1.5))),
|
Text(_isPublicRoom ? "Aspettiamo che uno sfidante si unisca dalla lobby pubblica." : "Attendi che il tuo amico accetti la sfida. Non chiudere questa finestra.", textAlign: TextAlign.center, style: getSharedTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.8), fontSize: 14, height: 1.5))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -274,13 +273,13 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
|
|
||||||
return StreamBuilder<DocumentSnapshot>(
|
return StreamBuilder<DocumentSnapshot>(
|
||||||
stream: _multiplayerService.listenToRoom(code),
|
stream: _multiplayerService.listenToRoom(code),
|
||||||
builder: (context, snapshot) {
|
builder: (ctx, snapshot) {
|
||||||
if (snapshot.hasData && snapshot.data!.exists) {
|
if (snapshot.hasData && snapshot.data!.exists) {
|
||||||
var data = snapshot.data!.data() as Map<String, dynamic>;
|
var data = snapshot.data!.data() as Map<String, dynamic>;
|
||||||
if (data['status'] == 'playing') {
|
if (data['status'] == 'playing') {
|
||||||
_roomStarted = true;
|
_roomStarted = true;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
Navigator.pop(context);
|
Navigator.pop(ctx);
|
||||||
context.read<GameController>().startNewGame(_selectedRadius, isOnline: true, roomCode: code, isHost: true, shape: _selectedShape, timeMode: _timeModeSetting);
|
context.read<GameController>().startNewGame(_selectedRadius, isOnline: true, roomCode: code, isHost: true, shape: _selectedShape, timeMode: _timeModeSetting);
|
||||||
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => const GameScreen()));
|
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => const GameScreen()));
|
||||||
});
|
});
|
||||||
|
|
@ -292,7 +291,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
onPopInvoked: (didPop) {
|
onPopInvoked: (didPop) {
|
||||||
if (didPop) return;
|
if (didPop) return;
|
||||||
_cleanupGhostRoom();
|
_cleanupGhostRoom();
|
||||||
Navigator.pop(context);
|
Navigator.pop(ctx);
|
||||||
},
|
},
|
||||||
child: Dialog(
|
child: Dialog(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
|
|
@ -305,9 +304,9 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_cleanupGhostRoom();
|
_cleanupGhostRoom();
|
||||||
Navigator.pop(context);
|
Navigator.pop(ctx);
|
||||||
},
|
},
|
||||||
child: Text(loc.btnCancel.toUpperCase(), style: getLobbyTextStyle(themeType, TextStyle(color: Colors.red, fontWeight: FontWeight.w900, fontSize: 20, letterSpacing: 2.0, shadows: themeType == AppThemeType.doodle ? [] : [const Shadow(color: Colors.black, blurRadius: 2)]))),
|
child: Text(loc.btnCancel.toUpperCase(), style: getSharedTextStyle(themeType, TextStyle(color: Colors.red, fontWeight: FontWeight.w900, fontSize: 20, letterSpacing: 2.0, shadows: themeType == AppThemeType.doodle ? [] : [const Shadow(color: Colors.black, blurRadius: 2)]))),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -350,7 +349,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
final themeManager = context.watch<ThemeManager>();
|
final themeManager = context.watch<ThemeManager>();
|
||||||
final themeType = themeManager.currentThemeType;
|
final themeType = themeManager.currentThemeType;
|
||||||
final theme = themeManager.currentColors;
|
final theme = themeManager.currentColors;
|
||||||
final loc = AppLocalizations.of(context)!; // <-- CHIAMATA AL DIZIONARIO
|
final loc = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
String? bgImage;
|
String? bgImage;
|
||||||
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg';
|
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg';
|
||||||
|
|
@ -731,4 +730,20 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FullScreenGridPainter extends CustomPainter {
|
||||||
|
final Color gridColor;
|
||||||
|
FullScreenGridPainter(this.gridColor);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final Paint paperGridPaint = Paint()..color = gridColor..strokeWidth = 1.0..style = PaintingStyle.stroke;
|
||||||
|
double paperStep = 20.0;
|
||||||
|
for (double i = 0; i <= size.width; i += paperStep) canvas.drawLine(Offset(i, 0), Offset(i, size.height), paperGridPaint);
|
||||||
|
for (double i = 0; i <= size.height; i += paperStep) canvas.drawLine(Offset(0, i), Offset(size.width, i), paperGridPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue