Agregada escritura y lectura del archivo de récords

parent e7c7e494
...@@ -4,4 +4,23 @@ class PeponatorRecord { ...@@ -4,4 +4,23 @@ class PeponatorRecord {
final DateTime fecha; final DateTime fecha;
PeponatorRecord({required this.jugador, required this.puntuacion, required this.fecha}); PeponatorRecord({required this.jugador, required this.puntuacion, required this.fecha});
factory PeponatorRecord.desdeJson(Map<String, dynamic> json) {
return PeponatorRecord(
jugador: json['jugador'],
puntuacion: json['puntuacion'],
fecha: DateTime.parse(json['fecha'])
);
}
String aJson(){
var json = '''
{
"jugador": "$jugador",
"puntuacion": $puntuacion,
"fecha": "$fecha"
}
''';
return json;
}
} }
\ No newline at end of file
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:peponator/modelo/peponator_record.dart';
import 'package:peponator/modelo/pista.dart'; import 'package:peponator/modelo/pista.dart';
import 'package:peponator/paginas/paginas.dart'; import 'package:peponator/paginas/paginas.dart';
import 'package:peponator/widgets/pantalla_pausa.dart'; import 'package:peponator/widgets/pantalla_pausa.dart';
...@@ -55,9 +60,14 @@ class _PantallaJuegoState extends State<PantallaJuego> ...@@ -55,9 +60,14 @@ class _PantallaJuegoState extends State<PantallaJuego>
late int posicion; late int posicion;
late String jugador; late String jugador;
late final File recordsFile;
List<PeponatorRecord>? records;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_loadRecords();
// TODO: Cargar el máximo de intentos // TODO: Cargar el máximo de intentos
maxIntentos = 21; maxIntentos = 21;
...@@ -640,7 +650,6 @@ class _PantallaJuegoState extends State<PantallaJuego> ...@@ -640,7 +650,6 @@ class _PantallaJuegoState extends State<PantallaJuego>
); );
} }
// TODO: Agregar una pantalla de registro de récords
Widget _buildPantallaFinal(Orientation orientation){ Widget _buildPantallaFinal(Orientation orientation){
final fullWidth = (orientation == Orientation.portrait)? final fullWidth = (orientation == Orientation.portrait)?
MediaQuery.of(context).size.width - MediaQuery.of(context).padding.horizontal : 400.0; MediaQuery.of(context).size.width - MediaQuery.of(context).padding.horizontal : 400.0;
...@@ -660,7 +669,7 @@ class _PantallaJuegoState extends State<PantallaJuego> ...@@ -660,7 +669,7 @@ class _PantallaJuegoState extends State<PantallaJuego>
Column( Column(
children: [ children: [
Text( Text(
'¡Nuevo récord!', puntuacion.toString(),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium, style: Theme.of(context).textTheme.headlineMedium,
), ),
...@@ -708,27 +717,60 @@ class _PantallaJuegoState extends State<PantallaJuego> ...@@ -708,27 +717,60 @@ class _PantallaJuegoState extends State<PantallaJuego>
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Expanded( Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: TextButton( child: TextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
backgroundColor: Colors.green, backgroundColor: Color.fromARGB(255, 0, 86, 3),
disabledBackgroundColor: Colors.white12, foregroundColor: Colors.white,
foregroundColor: Color.fromARGB(255, 237, 237, 237) disabledBackgroundColor: Colors.black26,
disabledForegroundColor: Color.fromARGB(255, 40, 40, 40),
), ),
onPressed: (jugador.isNotEmpty)? () {} : null, onPressed: (jugador.isNotEmpty)? () {
final newRecord = PeponatorRecord(
jugador: jugador,
puntuacion: puntuacion,
fecha: DateTime.now()
);
records!.insert(posicion, newRecord);
while(records!.length > PantallaRecords.maxRecords){
records!.removeLast();
}
_writeRecords();
setState(() {
posicion = PantallaRecords.maxRecords;
});
} : null,
child: Text( child: Text(
'Guardar récord', 'Guardar récord',
textScaler: TextScaler.linear(1.1), textScaler: TextScaler.linear(1.1),
) )
) ),
)
), ),
Expanded( Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: TextButton( child: TextButton(
onPressed: () {}, style: TextButton.styleFrom(
child: Text( backgroundColor: Color.fromARGB(255, 150, 10, 0),
'Salir sin guardar', foregroundColor: Colors.white,
textScaler: TextScaler.linear(1.1), disabledBackgroundColor: Colors.black26,
) disabledForegroundColor: Color.fromARGB(255, 40, 40, 40),
) ),
onPressed: () {
setState(() {
posicion = PantallaRecords.maxRecords;
});
},
child: Text(
'Salir sin guardar',
textScaler: TextScaler.linear(1.1),
)
),
)
) )
], ],
) )
...@@ -756,7 +798,7 @@ class _PantallaJuegoState extends State<PantallaJuego> ...@@ -756,7 +798,7 @@ class _PantallaJuegoState extends State<PantallaJuego>
), ),
const SizedBox(height: 16.0), const SizedBox(height: 16.0),
Text( Text(
victoria? '¿Quieres jugar otra vez?' : 'Más suerte la próxima vez...', victoria? 'Puntuación: $puntuacion' : 'Más suerte la próxima vez...',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.titleLarge,
), ),
...@@ -917,7 +959,7 @@ class _PantallaJuegoState extends State<PantallaJuego> ...@@ -917,7 +959,7 @@ class _PantallaJuegoState extends State<PantallaJuego>
void _escribirRespuesta() async { void _escribirRespuesta() async {
await Future.delayed(Duration(milliseconds: 600)); await Future.delayed(Duration(milliseconds: 600));
setState(() { setState(() {
// TODO: Construir respuesta de Peponator con cierto retardo // TODO: Construir respuesta de Peponator
mensajes.add(PeponatorMensaje(message: "¡Hola! Estoy pensando en un número del $limiteInferior al $limiteSuperior. ¿Te crees capaz de adivinarlo?")); mensajes.add(PeponatorMensaje(message: "¡Hola! Estoy pensando en un número del $limiteInferior al $limiteSuperior. ¿Te crees capaz de adivinarlo?"));
if(numeroEscogido! < numeroAdivinar){ if(numeroEscogido! < numeroAdivinar){
limiteInferior = numeroEscogido! + 1; limiteInferior = numeroEscogido! + 1;
...@@ -929,10 +971,16 @@ class _PantallaJuegoState extends State<PantallaJuego> ...@@ -929,10 +971,16 @@ class _PantallaJuegoState extends State<PantallaJuego>
} }
if(numeroEscogido! == numeroAdivinar){ if(numeroEscogido! == numeroAdivinar){
victoria = true; victoria = true;
// TODO: Calcular puntuación // TODO: Calcular puntuación
puntuacion = 2000; puntuacion = 1500;
// TODO: Calcular posición en la tabla de récords
posicion = 0; if(records != null){
posicion = records!.length;
while(posicion > 0 && records![posicion-1].puntuacion < puntuacion){
posicion--;
}
}
} }
numeroEscogido = null; numeroEscogido = null;
intentos++; intentos++;
...@@ -947,6 +995,40 @@ class _PantallaJuegoState extends State<PantallaJuego> ...@@ -947,6 +995,40 @@ class _PantallaJuegoState extends State<PantallaJuego>
_scrollDown(); _scrollDown();
}); });
} }
Future<void> _loadRecords() async {
recordsFile = await PantallaRecords.localFile();
final lista = <PeponatorRecord>[];
try {
final productosString = await recordsFile.readAsString();
print(productosString);
final List<dynamic> recordsJson = jsonDecode(productosString);
for (var prodJson in recordsJson) {
lista.add(PeponatorRecord.desdeJson(prodJson));
}
} on FileSystemException catch(e) {
lista.clear();
}
finally {
records = lista;
}
}
Future<void> _writeRecords() async {
final sb = StringBuffer('[\n');
for(int i = 0; i<records!.length; i++) {
sb.write(records![i].aJson());
if (i<records!.length-1) {
sb.write(',\n');
} else {
sb.write('\n');
}
}
sb.write(']');
await recordsFile.writeAsString(sb.toString());
}
} }
enum OpcionesFinPartida { enum OpcionesFinPartida {
......
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:peponator/modelo/modelo.dart'; import 'package:peponator/modelo/modelo.dart';
class PantallaRecords extends StatelessWidget { class PantallaRecords extends StatefulWidget {
static const int maxRecords = 20; static const int maxRecords = 20;
const PantallaRecords({super.key}); const PantallaRecords({super.key});
@override @override
Widget build(BuildContext context) { State<PantallaRecords> createState() => _PantallaRecordsState();
// TODO: Cargar los récords (esto se haría con un archivo de texto en la carpeta privada de la aplicación)
static Future<String> _localPath() async {
final Directory directory = await getApplicationDocumentsDirectory();
return directory.path;
}
static Future<File> localFile() async {
final path = await _localPath();
return File('$path/records.json');
}
}
class _PantallaRecordsState extends State<PantallaRecords> {
@override
Widget build(BuildContext context) {
return OrientationBuilder(builder: (context, orientation) { return OrientationBuilder(builder: (context, orientation) {
return Scaffold( return Scaffold(
body: SafeArea( body: SafeArea(
child: LayoutBuilder( child: FutureBuilder(
builder: (context, constraints) { future: _loadRecords(),
if(orientation == Orientation.portrait){ builder: (context, AsyncSnapshot<List<PeponatorRecord>> snapshot) {
return Column( if(snapshot.connectionState == ConnectionState.done){
crossAxisAlignment: CrossAxisAlignment.stretch, return LayoutBuilder(
children: [ builder: (context, constraints) {
_buildTitle(context, orientation), if(orientation == Orientation.portrait){
_buildRecordsView(context, orientation) return Column(
], crossAxisAlignment: CrossAxisAlignment.stretch,
); children: [
} _buildTitle(context, orientation),
else{ _buildRecordsView(context, orientation, snapshot.requireData)
return Row( ],
children: [ );
SizedBox( }
height: constraints.maxHeight, else{
width: constraints.maxWidth/4, return Row(
child: _buildTitle(context, orientation), children: [
), SizedBox(
SizedBox( height: constraints.maxHeight,
height: constraints.maxHeight, width: constraints.maxWidth/4,
width: constraints.maxWidth*3/4, child: _buildTitle(context, orientation),
child: _buildRecordsView(context, orientation), ),
) SizedBox(
], height: constraints.maxHeight,
); width: constraints.maxWidth*3/4,
} child: _buildRecordsView(context, orientation, snapshot.requireData),
} )
) ],
);
}
}
);
}
else{
return const Center(child: CircularProgressIndicator(),);
}
}
)
), ),
floatingActionButton: _buildFAB(context, orientation), floatingActionButton: _buildFAB(context, orientation),
); );
...@@ -80,9 +107,7 @@ class PantallaRecords extends StatelessWidget { ...@@ -80,9 +107,7 @@ class PantallaRecords extends StatelessWidget {
); );
} }
Widget _buildRecordsView(BuildContext context, Orientation orientation){ Widget _buildRecordsView(BuildContext context, Orientation orientation, List<PeponatorRecord> records){
final records = _loadMockData();
if(records.isNotEmpty) { if(records.isNotEmpty) {
final vistaRecords = ListView.builder( final vistaRecords = ListView.builder(
shrinkWrap: true, shrinkWrap: true,
...@@ -213,8 +238,8 @@ class PantallaRecords extends StatelessWidget { ...@@ -213,8 +238,8 @@ class PantallaRecords extends StatelessWidget {
)), )),
content: Text( content: Text(
(orientation == Orientation.portrait)? (orientation == Orientation.portrait)?
'Esta acción no se puede deshacer. ¿Seguro que quieres borrar todos los récords?': 'Esta acción no se puede deshacer. ¿Seguro que quieres borrar todos los récords?':
'Esta acción no se puede deshacer.\n¿Seguro que quieres borrar todos los récords?' 'Esta acción no se puede deshacer.\n¿Seguro que quieres borrar todos los récords?'
, ,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium, style: Theme.of(context).textTheme.titleMedium,
...@@ -223,7 +248,7 @@ class PantallaRecords extends StatelessWidget { ...@@ -223,7 +248,7 @@ class PantallaRecords extends StatelessWidget {
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
// TODO: Borrar récords _deleteRecords();
Navigator.pop(context); Navigator.pop(context);
}, },
style: TextButton.styleFrom( style: TextButton.styleFrom(
...@@ -276,50 +301,27 @@ class PantallaRecords extends StatelessWidget { ...@@ -276,50 +301,27 @@ class PantallaRecords extends StatelessWidget {
); );
} }
List<PeponatorRecord> _loadMockData() { Future<List<PeponatorRecord>> _loadRecords() async {
final records = <PeponatorRecord>[]; final file = await PantallaRecords.localFile();
final lista = <PeponatorRecord>[];
records.add(PeponatorRecord( try {
jugador: "AAA", final productosString = await file.readAsString();
puntuacion: 12211350, print(productosString);
fecha: DateTime.now() final List<dynamic> recordsJson = jsonDecode(productosString);
)); for (var prodJson in recordsJson) {
records.add(PeponatorRecord( lista.add(PeponatorRecord.desdeJson(prodJson));
jugador: "BBB", }
puntuacion: 25, } on FileSystemException catch(e) {
fecha: DateTime.now() lista.clear();
)); }
records.add(PeponatorRecord(
jugador: "CCC", return lista;
puntuacion: 13, }
fecha: DateTime.now()
));
records.add(PeponatorRecord(
jugador: "DDD",
puntuacion: 7,
fecha: DateTime.now()
));
records.add(PeponatorRecord(
jugador: "EEE",
puntuacion: 6,
fecha: DateTime.now()
));
records.add(PeponatorRecord(
jugador: "FFF",
puntuacion: 5,
fecha: DateTime.now()
));
records.add(PeponatorRecord(
jugador: "GGG",
puntuacion: 4,
fecha: DateTime.now()
));
records.add(PeponatorRecord(
jugador: "HHH",
puntuacion: 3,
fecha: DateTime.now()
));
return records; Future<void> _deleteRecords() async {
final file = await PantallaRecords.localFile();
await file.delete();
setState(() {});
} }
} }
\ No newline at end of file
...@@ -17,7 +17,7 @@ class PeponatorApp extends StatelessWidget { ...@@ -17,7 +17,7 @@ class PeponatorApp extends StatelessWidget {
), ),
darkTheme: ThemeData.dark(), darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system, themeMode: ThemeMode.system,
home: PantallaJuego(fromHome: true) home: PantallaRecords()
); );
} }
} }
...@@ -5,8 +5,10 @@ ...@@ -5,8 +5,10 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import path_provider_foundation
import shared_preferences_foundation import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
} }
...@@ -160,6 +160,30 @@ packages: ...@@ -160,6 +160,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.9.1"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
url: "https://pub.dev"
source: hosted
version: "2.2.17"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
......
...@@ -35,6 +35,7 @@ dependencies: ...@@ -35,6 +35,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
shared_preferences: ^2.5.3 shared_preferences: ^2.5.3
path_provider: ^2.1.5
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment