Agregada escritura y lectura del archivo de récords

parent e7c7e494
......@@ -4,4 +4,23 @@ class PeponatorRecord {
final DateTime 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:peponator/modelo/peponator_record.dart';
import 'package:peponator/modelo/pista.dart';
import 'package:peponator/paginas/paginas.dart';
import 'package:peponator/widgets/pantalla_pausa.dart';
......@@ -55,9 +60,14 @@ class _PantallaJuegoState extends State<PantallaJuego>
late int posicion;
late String jugador;
late final File recordsFile;
List<PeponatorRecord>? records;
@override
void initState() {
super.initState();
_loadRecords();
// TODO: Cargar el máximo de intentos
maxIntentos = 21;
......@@ -640,7 +650,6 @@ class _PantallaJuegoState extends State<PantallaJuego>
);
}
// TODO: Agregar una pantalla de registro de récords
Widget _buildPantallaFinal(Orientation orientation){
final fullWidth = (orientation == Orientation.portrait)?
MediaQuery.of(context).size.width - MediaQuery.of(context).padding.horizontal : 400.0;
......@@ -660,7 +669,7 @@ class _PantallaJuegoState extends State<PantallaJuego>
Column(
children: [
Text(
'¡Nuevo récord!',
puntuacion.toString(),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium,
),
......@@ -708,27 +717,60 @@ class _PantallaJuegoState extends State<PantallaJuego>
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.green,
disabledBackgroundColor: Colors.white12,
foregroundColor: Color.fromARGB(255, 237, 237, 237)
backgroundColor: Color.fromARGB(255, 0, 86, 3),
foregroundColor: Colors.white,
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(
'Guardar récord',
textScaler: TextScaler.linear(1.1),
)
)
),
)
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: TextButton(
onPressed: () {},
child: Text(
'Salir sin guardar',
textScaler: TextScaler.linear(1.1),
)
)
style: TextButton.styleFrom(
backgroundColor: Color.fromARGB(255, 150, 10, 0),
foregroundColor: Colors.white,
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>
),
const SizedBox(height: 16.0),
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,
style: Theme.of(context).textTheme.titleLarge,
),
......@@ -917,7 +959,7 @@ class _PantallaJuegoState extends State<PantallaJuego>
void _escribirRespuesta() async {
await Future.delayed(Duration(milliseconds: 600));
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?"));
if(numeroEscogido! < numeroAdivinar){
limiteInferior = numeroEscogido! + 1;
......@@ -929,10 +971,16 @@ class _PantallaJuegoState extends State<PantallaJuego>
}
if(numeroEscogido! == numeroAdivinar){
victoria = true;
// TODO: Calcular puntuación
puntuacion = 2000;
// TODO: Calcular posición en la tabla de récords
posicion = 0;
puntuacion = 1500;
if(records != null){
posicion = records!.length;
while(posicion > 0 && records![posicion-1].puntuacion < puntuacion){
posicion--;
}
}
}
numeroEscogido = null;
intentos++;
......@@ -947,6 +995,40 @@ class _PantallaJuegoState extends State<PantallaJuego>
_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 {
......
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:peponator/modelo/modelo.dart';
class PantallaRecords extends StatelessWidget {
class PantallaRecords extends StatefulWidget {
static const int maxRecords = 20;
const PantallaRecords({super.key});
@override
Widget build(BuildContext context) {
// TODO: Cargar los récords (esto se haría con un archivo de texto en la carpeta privada de la aplicación)
State<PantallaRecords> createState() => _PantallaRecordsState();
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 Scaffold(
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
if(orientation == Orientation.portrait){
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildTitle(context, orientation),
_buildRecordsView(context, orientation)
],
);
}
else{
return Row(
children: [
SizedBox(
height: constraints.maxHeight,
width: constraints.maxWidth/4,
child: _buildTitle(context, orientation),
),
SizedBox(
height: constraints.maxHeight,
width: constraints.maxWidth*3/4,
child: _buildRecordsView(context, orientation),
)
],
);
}
}
)
child: FutureBuilder(
future: _loadRecords(),
builder: (context, AsyncSnapshot<List<PeponatorRecord>> snapshot) {
if(snapshot.connectionState == ConnectionState.done){
return LayoutBuilder(
builder: (context, constraints) {
if(orientation == Orientation.portrait){
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildTitle(context, orientation),
_buildRecordsView(context, orientation, snapshot.requireData)
],
);
}
else{
return Row(
children: [
SizedBox(
height: constraints.maxHeight,
width: constraints.maxWidth/4,
child: _buildTitle(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),
);
......@@ -80,9 +107,7 @@ class PantallaRecords extends StatelessWidget {
);
}
Widget _buildRecordsView(BuildContext context, Orientation orientation){
final records = _loadMockData();
Widget _buildRecordsView(BuildContext context, Orientation orientation, List<PeponatorRecord> records){
if(records.isNotEmpty) {
final vistaRecords = ListView.builder(
shrinkWrap: true,
......@@ -213,8 +238,8 @@ class PantallaRecords extends StatelessWidget {
)),
content: Text(
(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.\n¿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?'
,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium,
......@@ -223,7 +248,7 @@ class PantallaRecords extends StatelessWidget {
actions: [
TextButton(
onPressed: () {
// TODO: Borrar récords
_deleteRecords();
Navigator.pop(context);
},
style: TextButton.styleFrom(
......@@ -276,50 +301,27 @@ class PantallaRecords extends StatelessWidget {
);
}
List<PeponatorRecord> _loadMockData() {
final records = <PeponatorRecord>[];
Future<List<PeponatorRecord>> _loadRecords() async {
final file = await PantallaRecords.localFile();
final lista = <PeponatorRecord>[];
records.add(PeponatorRecord(
jugador: "AAA",
puntuacion: 12211350,
fecha: DateTime.now()
));
records.add(PeponatorRecord(
jugador: "BBB",
puntuacion: 25,
fecha: DateTime.now()
));
records.add(PeponatorRecord(
jugador: "CCC",
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()
));
try {
final productosString = await file.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();
}
return lista;
}
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 {
),
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system,
home: PantallaJuego(fromHome: true)
home: PantallaRecords()
);
}
}
......@@ -5,8 +5,10 @@
import FlutterMacOS
import Foundation
import path_provider_foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}
......@@ -160,6 +160,30 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
......
......@@ -35,6 +35,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
shared_preferences: ^2.5.3
path_provider: ^2.1.5
dev_dependencies:
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