Página de dificultades estilizada y pequeñas correcciones

parent a2213ba5
...@@ -8,28 +8,39 @@ import 'dart:math'; ...@@ -8,28 +8,39 @@ import 'dart:math';
/// Dado un número de intentos, conseguir adivinar un número en una /// Dado un número de intentos, conseguir adivinar un número en una
/// dificultad mayor debería de dar una puntuación mayor. /// dificultad mayor debería de dar una puntuación mayor.
class Dificultad { class Dificultad {
// TODO: SISTEMA PARA CALCULAR PUNTUACIÓN. Pensar en log2(limite)
final String _id;
final int _limite; final int _limite;
final int _maxIntentos; final int _maxIntentos;
final String _nombre; final String _nombre;
const Dificultad({required limite, required maxIntentos, required nombre}): const Dificultad({
_limite = limite, _maxIntentos = maxIntentos, _nombre = nombre; required String id,
required int limite,
required int maxIntentos,
required String nombre
}):
_id = id, _limite = limite, _maxIntentos = maxIntentos, _nombre = nombre;
String get id => _id;
int get limite => _limite; int get limite => _limite;
int get maxIntentos => _maxIntentos; int get maxIntentos => _maxIntentos;
String get nombre => _nombre; String get nombre => _nombre;
factory Dificultad.desdeJson(Map<String, dynamic> json) { factory Dificultad.desdeJson(Map<String, dynamic> json) {
return Dificultad( return Dificultad(
limite: int.parse(json['limite']), id: json['id'],
maxIntentos: int.parse(json['maxIntentos']), limite: int.parse(json['limite']),
nombre: json['nombre'] maxIntentos: int.parse(json['maxIntentos']),
nombre: json['nombre']
); );
} }
String toJson() { String toJson() {
return ''' return '''
{ {
"id": "$id",
"limite": "$limite", "limite": "$limite",
"maxIntentos": "$maxIntentos", "maxIntentos": "$maxIntentos",
"nombre": "$nombre" "nombre": "$nombre"
...@@ -37,19 +48,23 @@ class Dificultad { ...@@ -37,19 +48,23 @@ class Dificultad {
'''; ''';
} }
// Los números de intentos de estas dificultades están calculados como log2(limite)+1
static final Dificultad facil = Dificultad( static final Dificultad facil = Dificultad(
id: '4f3ad59c-31b3-11f0-9cd2-0242ac120002',
limite: 100, limite: 100,
maxIntentos: 7+1, maxIntentos: 7+1,
nombre: "Fácil"); nombre: 'Fácil');
static final Dificultad normal = Dificultad( static final Dificultad normal = Dificultad(
id: '4f3ad736-31b3-11f0-9cd2-0242ac120002',
limite: 500, limite: 500,
maxIntentos: 9+1, maxIntentos: 9+1,
nombre: "Normal" nombre: 'Normal'
); );
static final Dificultad dificil = Dificultad( static final Dificultad dificil = Dificultad(
id: '4f3ad7f4-31b3-11f0-9cd2-0242ac120002',
limite: 1000, limite: 1000,
maxIntentos: 10+1, maxIntentos: 10+1,
nombre: "Dificil" nombre: 'Difícil'
); );
int generarNumero() => Random().nextInt(limite) + 1; int generarNumero() => Random().nextInt(limite) + 1;
...@@ -70,12 +85,13 @@ class Dificultad { ...@@ -70,12 +85,13 @@ class Dificultad {
factory Dificultad.copia(Dificultad otro) { factory Dificultad.copia(Dificultad otro) {
return Dificultad( return Dificultad(
id: otro.id,
limite: otro.limite, limite: otro.limite,
maxIntentos: otro.maxIntentos, maxIntentos: otro.maxIntentos,
nombre: otro.nombre nombre: otro.nombre
); );
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (other is! Dificultad) { if (other is! Dificultad) {
......
...@@ -14,7 +14,6 @@ class ListaDificultad extends ChangeNotifier { ...@@ -14,7 +14,6 @@ class ListaDificultad extends ChangeNotifier {
int _indiceSeleccionado = 0; int _indiceSeleccionado = 0;
bool _cargando = true; bool _cargando = true;
ListaDificultad() { ListaDificultad() {
_cargarDificultades(); _cargarDificultades();
} }
......
...@@ -13,12 +13,45 @@ class PantallaDificultad extends StatelessWidget { ...@@ -13,12 +13,45 @@ class PantallaDificultad extends StatelessWidget {
child: manager.listo child: manager.listo
? ListView.separated( ? ListView.separated(
itemBuilder: (context, index) { itemBuilder: (context, index) {
return DificultadWidget(indice: index,); Color? color;
Color? text;
if(index == 0){
color = (Theme.of(context).brightness == Brightness.dark)?
Color.fromARGB(255, 0, 86, 3) :
Colors.lightGreenAccent.shade100;
text = (Theme.of(context).brightness == Brightness.dark)?
Colors.white :
Colors.black;
}
else if(index == 1){
color = (Theme.of(context).brightness == Brightness.dark)?
Colors.blue.shade800 :
Colors.blue.shade200;
text = (Theme.of(context).brightness == Brightness.dark)?
Colors.white :
Colors.black;
}
else if(index == 2){
color = (Theme.of(context).brightness == Brightness.dark)?
Color.fromARGB(255, 150, 10, 0) :
Colors.red.shade300;
text = (Theme.of(context).brightness == Brightness.dark)?
Colors.white :
Colors.black;
}
return DificultadWidget(
key: Key(manager.dificultades[index].id),
staticDif: index < 3,
indice: index,
backgroundColor: color,
foregroundColor: text,
);
}, },
separatorBuilder: (context, index) => const SizedBox(height: 8), separatorBuilder: (context, index) => const SizedBox(height: 8),
itemCount: manager.length itemCount: manager.length
) )
: CircularProgressIndicator() : Center(child: CircularProgressIndicator())
); );
} }
); );
...@@ -32,7 +65,7 @@ class PantallaDificultad extends StatelessWidget { ...@@ -32,7 +65,7 @@ class PantallaDificultad extends StatelessWidget {
Dificultad seleccionada = Provider.of<ListaDificultad>(context, listen: false).seleccionada; Dificultad seleccionada = Provider.of<ListaDificultad>(context, listen: false).seleccionada;
Navigator.maybePop(context, seleccionada); Navigator.maybePop(context, seleccionada);
}), }),
title: const Text("Selecciona dificultad"), title: const Text('Selecciona dificultad'),
), ),
body: construirListaDificultad(), body: construirListaDificultad(),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
......
...@@ -20,7 +20,7 @@ class PeponatorApp extends StatelessWidget { ...@@ -20,7 +20,7 @@ class PeponatorApp extends StatelessWidget {
), ),
darkTheme: ThemeData.dark(), darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system, themeMode: ThemeMode.system,
home: PantallaJuego(fromHome: true), home: PantallaJuego(),
), ),
); );
} }
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:peponator/modelo/dificultad.dart'; import 'package:peponator/modelo/dificultad.dart';
import 'package:uuid/uuid.dart';
class DificultadDialog extends StatefulWidget { class DificultadDialog extends StatefulWidget {
final Dificultad? dificultad; final Dificultad? dificultad;
...@@ -11,27 +12,85 @@ class DificultadDialog extends StatefulWidget { ...@@ -11,27 +12,85 @@ class DificultadDialog extends StatefulWidget {
State<DificultadDialog> createState() => _DificultadDialogState(); State<DificultadDialog> createState() => _DificultadDialogState();
} }
// TODO: HACER QUE 'HECHO' SE DESABILITE CUANDO CAMPOS VACIOS
// TODO: MEJORAR EL ASPECTO
// TODO: CAMBIAR PARA USAR IDIOMA // TODO: CAMBIAR PARA USAR IDIOMA
class _DificultadDialogState extends State<DificultadDialog> { class _DificultadDialogState extends State<DificultadDialog> {
late TextEditingController limiteController;
late TextEditingController intentosController;
late TextEditingController nombreController;
int? limite;
int? intentos;
String? nombre;
String? errorLimite;
String? errorIntentos;
String? errorNombre;
@override @override
Widget build(BuildContext context) { void initState() {
TextEditingController limiteController = TextEditingController( super.initState();
limiteController = TextEditingController(
text: widget.dificultad?.limite.toString() text: widget.dificultad?.limite.toString()
); );
TextEditingController intentosController = TextEditingController( limiteController.addListener(() =>
text: widget.dificultad?.maxIntentos.toString() setState(() {
errorLimite = calculateErrorLimite(limiteController.text);
limite = int.tryParse(limiteController.text);
})
);
if(limiteController.text.isEmpty){
errorLimite = calculateErrorLimite(limiteController.text);
}
intentosController = TextEditingController(
text: widget.dificultad?.maxIntentos.toString()
);
intentosController.addListener(() =>
setState(() {
errorIntentos = calculateErrorIntentos(intentosController.text);
intentos = int.tryParse(intentosController.text);
})
);
if(intentosController.text.isEmpty){
errorIntentos = calculateErrorLimite(intentosController.text);
}
nombreController = TextEditingController(
text: widget.dificultad?.nombre.toString()
); );
TextEditingController nombreController = TextEditingController( nombreController.addListener(() =>
text: widget.dificultad?.nombre setState(() {
nombre = nombreController.text;
if(nombre == null || nombre!.isEmpty){
errorNombre = 'El nombre no puede estar vacío';
}
else{
errorNombre = null;
}
})
); );
if(nombreController.text.isEmpty){
errorNombre = 'El nombre no puede estar vacío';
}
}
@override
void dispose() {
limiteController.dispose();
intentosController.dispose();
nombreController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SimpleDialog( return SimpleDialog(
title: Text( title: Text(
widget.dificultad != null widget.dificultad != null
? "Editar dificultad" ? "Editar dificultad"
: "Añadir dificultad" : "Añadir dificultad",
textAlign: TextAlign.center
), ),
children: [ children: [
SimpleDialogOption( SimpleDialogOption(
...@@ -43,6 +102,10 @@ class _DificultadDialogState extends State<DificultadDialog> { ...@@ -43,6 +102,10 @@ class _DificultadDialogState extends State<DificultadDialog> {
controller: limiteController, controller: limiteController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
border: OutlineInputBorder(),
errorText: errorLimite,
),
), ),
const SizedBox(height: 16,), const SizedBox(height: 16,),
Text("Número de intentos"), Text("Número de intentos"),
...@@ -50,10 +113,21 @@ class _DificultadDialogState extends State<DificultadDialog> { ...@@ -50,10 +113,21 @@ class _DificultadDialogState extends State<DificultadDialog> {
controller: intentosController, controller: intentosController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
border: OutlineInputBorder(),
errorText: errorIntentos
),
), ),
const SizedBox(height: 16,), const SizedBox(height: 16,),
Text("Nombre"), Text("Nombre"),
TextField(controller: nombreController), TextField(
controller: nombreController,
maxLength: 8,
decoration: InputDecoration(
border: OutlineInputBorder(),
errorText: errorNombre
),
),
], ],
), ),
), ),
...@@ -61,27 +135,86 @@ class _DificultadDialogState extends State<DificultadDialog> { ...@@ -61,27 +135,86 @@ class _DificultadDialogState extends State<DificultadDialog> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
SimpleDialogOption( Expanded(
onPressed: () { child: SimpleDialogOption(
Navigator.pop(context, child: TextButton(
Dificultad( style: TextButton.styleFrom(
limite: int.parse(limiteController.text), backgroundColor: Color.fromARGB(255, 0, 86, 3),
maxIntentos: int.parse(intentosController.text), foregroundColor: Colors.white,
nombre: nombreController.text, disabledBackgroundColor: Colors.black26,
) disabledForegroundColor: Color.fromARGB(255, 40, 40, 40),
); ),
}, onPressed: (errorLimite != null || errorIntentos != null || errorNombre != null)?
child: Text("Hecho"), null : () {
if(errorLimite == null && errorIntentos == null){
Navigator.pop(context,
Dificultad(
id: (widget.dificultad == null)? Uuid().v1() : widget.dificultad!.id,
limite: int.parse(limiteController.text),
maxIntentos: int.parse(intentosController.text),
nombre: nombreController.text,
)
);
}
},
child: Text(
"Hecho",
textScaler: TextScaler.linear(1.1),
)
),
),
), ),
SimpleDialogOption( Expanded(
onPressed: () => Navigator.pop(context), child: SimpleDialogOption(
child: Text("Cancelar"), child: TextButton(
style: TextButton.styleFrom(
backgroundColor: Color.fromARGB(255, 150, 10, 0),
foregroundColor: Colors.white,
disabledBackgroundColor: Colors.black26,
disabledForegroundColor: Color.fromARGB(255, 40, 40, 40),
),
onPressed: () => Navigator.pop(context),
child: Text(
'Cancelar',
textScaler: TextScaler.linear(1.1),
)
)
),
) )
], ],
), ),
) )
], ],
); );
} }
String? calculateErrorLimite(String num){
try {
int i = int.parse(num);
if(i < 2){
return 'El límite mínimo es 2';
}
} on Exception {
if(num.isEmpty){
return 'Por favor, rellena este campo';
}
return 'El número es demasiado grande';
}
return null;
}
String? calculateErrorIntentos(String num){
try {
int i = int.parse(num);
if(i < 1){
return 'El número mínimo de intentos es 1';
}
} on Exception {
if(num.isEmpty){
return 'Por favor, rellena este campo';
}
return 'El número es demasiado grande';
}
return null;
}
} }
import 'package:peponator/widgets/peponator_mensaje.dart'; import 'package:peponator/widgets/peponator_mensaje.dart';
abstract class PeponetorMensajeFactory { abstract class PeponatorMensajeFactory {
static PeponatorMensaje mensajeInicial(int inferior, int superior) { static PeponatorMensaje mensajeInicial(int inferior, int superior) {
return PeponatorMensaje( return PeponatorMensaje(
message: "¡Hola! Estoy pensando en un número del $inferior" message: "¡Hola! Estoy pensando en un número del $inferior"
...@@ -11,7 +11,7 @@ abstract class PeponetorMensajeFactory { ...@@ -11,7 +11,7 @@ abstract class PeponetorMensajeFactory {
static PeponatorMensaje respuesta(int intento, int numAdivinar) { static PeponatorMensaje respuesta(int intento, int numAdivinar) {
StringBuffer sb = StringBuffer("El $intento está por "); StringBuffer sb = StringBuffer("El $intento está por ");
sb.write(intento < numAdivinar ? "debajo. " : "encima. "); sb.write(intento < numAdivinar ? "debajo. " : "encima. ");
sb.write("\nSigue intentándolo!"); sb.write("\n¡Sigue intentándolo!");
return PeponatorMensaje(message: sb.toString()); return PeponatorMensaje(message: sb.toString());
} }
...@@ -19,15 +19,15 @@ abstract class PeponetorMensajeFactory { ...@@ -19,15 +19,15 @@ abstract class PeponetorMensajeFactory {
static PeponatorMensaje victoria(int numIntentos, int numPistas) { static PeponatorMensaje victoria(int numIntentos, int numPistas) {
String s1 = numIntentos > 1 ? "s" : ""; String s1 = numIntentos > 1 ? "s" : "";
String s2 = numPistas > 1 ? "s" : ""; String s2 = numPistas > 1 ? "s" : "";
StringBuffer sb = StringBuffer("Felicidades! Lo has conseguido en $numIntentos intento$s1"); StringBuffer sb = StringBuffer("¡Felicidades! ¡Lo has conseguido en $numIntentos intento$s1");
sb.write(numPistas == 0 ? "y sin pistas!" : "usando $numPistas pistas$s2"); sb.write(numPistas == 0 ? " y sin pistas!" : " usando $numPistas pista$s2!");
return PeponatorMensaje(message: sb.toString()); return PeponatorMensaje(message: sb.toString());
} }
static PeponatorMensaje derrota() { static PeponatorMensaje derrota() {
return PeponatorMensaje( return PeponatorMensaje(
message: "Que pena! La próxima lo conseguirás" message: "¡Qué pena! A la próxima seguro que lo consigues"
); );
} }
} }
\ No newline at end of file
...@@ -3,26 +3,42 @@ import 'package:provider/provider.dart'; ...@@ -3,26 +3,42 @@ import 'package:provider/provider.dart';
import 'package:peponator/modelo/modelo.dart'; import 'package:peponator/modelo/modelo.dart';
import 'package:peponator/widgets/dificultad_dialog.dart'; import 'package:peponator/widgets/dificultad_dialog.dart';
// TODO: HACER QUE SEA MENOS FEO
// TODO: MARCAR DIFICULTAD SELECCIONADA
class DificultadWidget extends StatelessWidget { class DificultadWidget extends StatelessWidget {
final bool _staticDif;
final int indice; final int indice;
final Color? backgroundColor;
final Color? foregroundColor;
const DificultadWidget({super.key, required this.indice}); const DificultadWidget({
required super.key,
staticDif = false,
required this.indice,
this.backgroundColor,
this.foregroundColor
}): _staticDif = staticDif;
bool get staticDif => _staticDif;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ListaDificultad listaDificultad = Provider.of(context, listen: false); ListaDificultad listaDificultad = Provider.of(context, listen: false);
Dificultad dificultad = listaDificultad.get(indice); Dificultad dificultad = listaDificultad.get(indice);
bool seleccionado = indice == listaDificultad.indiceSeleccionado; bool seleccionado = indice == listaDificultad.indiceSeleccionado;
Color bg = backgroundColor ?? ((Theme.of(context).brightness == Brightness.light)? Colors.grey.shade300 : Colors.grey.shade800);
return Container( final interior = Container(
decoration: seleccionado decoration: seleccionado?
? BoxDecoration(border: Border.all(color: Colors.red)) BoxDecoration(
: null, border: Border.all(
child: GestureDetector( color: (Theme.of(context).brightness == Brightness.dark)?
Colors.white : Colors.black,
width: 3.0
)
) : null,
child: InkWell(
splashFactory: InkRipple.splashFactory,
onTap: () => listaDificultad.select(indice), onTap: () => listaDificultad.select(indice),
onLongPress: () async { onLongPress: (staticDif)? null : () async {
Dificultad? cambiado = await Dificultad? cambiado = await
showDialog<Dificultad>( showDialog<Dificultad>(
context: context, context: context,
...@@ -32,27 +48,93 @@ class DificultadWidget extends StatelessWidget { ...@@ -32,27 +48,93 @@ class DificultadWidget extends StatelessWidget {
listaDificultad.update(indice, cambiado); listaDificultad.update(indice, cambiado);
} }
}, },
child: Row( child: Padding(
mainAxisAlignment: MainAxisAlignment.spaceBetween, padding: EdgeInsets.only(top: (seleccionado)? 13.0 : 16.0, bottom: (seleccionado)? 13.0 : 16.0),
children: [ child: Row(
Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const SizedBox(width: 8, height: 16,), SizedBox(
Text( width: MediaQuery.of(context).size.width/3 - 3,
dificultad.nombre, child: Padding(
style: Theme.of(context).textTheme.titleLarge, padding: EdgeInsets.only(left: (seleccionado)? 5.0 : 8.0),
child: Text(
dificultad.nombre,
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: foregroundColor
),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width/3 - 3,
child: Text(
'${dificultad.maxIntentos} intentos',
textAlign: TextAlign.center,
style: TextStyle(
color: foregroundColor
),
)
),
SizedBox(
width: MediaQuery.of(context).size.width/3 - 3,
child: Padding(
padding: EdgeInsets.only(right: (seleccionado)? 5.0 : 8.0),
child: Text(
'1 - ${dificultad.limite}',
textAlign: TextAlign.right,
style: TextStyle(
color: foregroundColor
),
),
), ),
], ),
],
),
),
),
);
if(staticDif){
return Material(
color: bg,
child: interior
);
}
return Material(
color: bg,
child: Dismissible(
key: key!,
background: Container(
color: Colors.red,
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Icon(
Icons.delete,
color: Colors.white,
size: 32.0,
), ),
Text("Intentos: ${dificultad.maxIntentos}"), ),
Row( ),
children: [ secondaryBackground: Container(
Text("1 - ${dificultad.limite}"), color: Colors.red,
const SizedBox(width: 8, height: 16,), alignment: Alignment.centerRight,
], child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(
Icons.delete,
color: Colors.white,
size: 32.0,
), ),
], ),
), ),
onDismissed: (direction) {
if(seleccionado) listaDificultad.select(0);
listaDificultad.delete(indice);
},
child: interior
), ),
); );
} }
......
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