Página de dificultades estilizada y pequeñas correcciones

parent a2213ba5
......@@ -8,28 +8,39 @@ import 'dart:math';
/// Dado un número de intentos, conseguir adivinar un número en una
/// dificultad mayor debería de dar una puntuación mayor.
class Dificultad {
// TODO: SISTEMA PARA CALCULAR PUNTUACIÓN. Pensar en log2(limite)
final String _id;
final int _limite;
final int _maxIntentos;
final String _nombre;
const Dificultad({required limite, required maxIntentos, required nombre}):
_limite = limite, _maxIntentos = maxIntentos, _nombre = nombre;
const Dificultad({
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 maxIntentos => _maxIntentos;
String get nombre => _nombre;
factory Dificultad.desdeJson(Map<String, dynamic> json) {
return Dificultad(
limite: int.parse(json['limite']),
maxIntentos: int.parse(json['maxIntentos']),
nombre: json['nombre']
id: json['id'],
limite: int.parse(json['limite']),
maxIntentos: int.parse(json['maxIntentos']),
nombre: json['nombre']
);
}
String toJson() {
return '''
{
"id": "$id",
"limite": "$limite",
"maxIntentos": "$maxIntentos",
"nombre": "$nombre"
......@@ -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(
id: '4f3ad59c-31b3-11f0-9cd2-0242ac120002',
limite: 100,
maxIntentos: 7+1,
nombre: "Fácil");
nombre: 'Fácil');
static final Dificultad normal = Dificultad(
id: '4f3ad736-31b3-11f0-9cd2-0242ac120002',
limite: 500,
maxIntentos: 9+1,
nombre: "Normal"
nombre: 'Normal'
);
static final Dificultad dificil = Dificultad(
id: '4f3ad7f4-31b3-11f0-9cd2-0242ac120002',
limite: 1000,
maxIntentos: 10+1,
nombre: "Dificil"
nombre: 'Difícil'
);
int generarNumero() => Random().nextInt(limite) + 1;
......@@ -70,12 +85,13 @@ class Dificultad {
factory Dificultad.copia(Dificultad otro) {
return Dificultad(
id: otro.id,
limite: otro.limite,
maxIntentos: otro.maxIntentos,
nombre: otro.nombre
);
}
@override
bool operator ==(Object other) {
if (other is! Dificultad) {
......
......@@ -14,7 +14,6 @@ class ListaDificultad extends ChangeNotifier {
int _indiceSeleccionado = 0;
bool _cargando = true;
ListaDificultad() {
_cargarDificultades();
}
......
......@@ -13,12 +13,45 @@ class PantallaDificultad extends StatelessWidget {
child: manager.listo
? ListView.separated(
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),
itemCount: manager.length
)
: CircularProgressIndicator()
: Center(child: CircularProgressIndicator())
);
}
);
......@@ -32,7 +65,7 @@ class PantallaDificultad extends StatelessWidget {
Dificultad seleccionada = Provider.of<ListaDificultad>(context, listen: false).seleccionada;
Navigator.maybePop(context, seleccionada);
}),
title: const Text("Selecciona dificultad"),
title: const Text('Selecciona dificultad'),
),
body: construirListaDificultad(),
floatingActionButton: FloatingActionButton(
......
......@@ -20,7 +20,7 @@ class PeponatorApp extends StatelessWidget {
),
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system,
home: PantallaJuego(fromHome: true),
home: PantallaJuego(),
),
);
}
......
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:peponator/modelo/dificultad.dart';
import 'package:uuid/uuid.dart';
class DificultadDialog extends StatefulWidget {
final Dificultad? dificultad;
......@@ -11,27 +12,85 @@ class DificultadDialog extends StatefulWidget {
State<DificultadDialog> createState() => _DificultadDialogState();
}
// TODO: HACER QUE 'HECHO' SE DESABILITE CUANDO CAMPOS VACIOS
// TODO: MEJORAR EL ASPECTO
// TODO: CAMBIAR PARA USAR IDIOMA
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
Widget build(BuildContext context) {
TextEditingController limiteController = TextEditingController(
void initState() {
super.initState();
limiteController = TextEditingController(
text: widget.dificultad?.limite.toString()
);
TextEditingController intentosController = TextEditingController(
text: widget.dificultad?.maxIntentos.toString()
limiteController.addListener(() =>
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(
text: widget.dificultad?.nombre
nombreController.addListener(() =>
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(
title: Text(
widget.dificultad != null
? "Editar dificultad"
: "Añadir dificultad"
: "Añadir dificultad",
textAlign: TextAlign.center
),
children: [
SimpleDialogOption(
......@@ -43,6 +102,10 @@ class _DificultadDialogState extends State<DificultadDialog> {
controller: limiteController,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
border: OutlineInputBorder(),
errorText: errorLimite,
),
),
const SizedBox(height: 16,),
Text("Número de intentos"),
......@@ -50,10 +113,21 @@ class _DificultadDialogState extends State<DificultadDialog> {
controller: intentosController,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
border: OutlineInputBorder(),
errorText: errorIntentos
),
),
const SizedBox(height: 16,),
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> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SimpleDialogOption(
onPressed: () {
Navigator.pop(context,
Dificultad(
limite: int.parse(limiteController.text),
maxIntentos: int.parse(intentosController.text),
nombre: nombreController.text,
)
);
},
child: Text("Hecho"),
Expanded(
child: SimpleDialogOption(
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: Color.fromARGB(255, 0, 86, 3),
foregroundColor: Colors.white,
disabledBackgroundColor: Colors.black26,
disabledForegroundColor: Color.fromARGB(255, 40, 40, 40),
),
onPressed: (errorLimite != null || errorIntentos != null || errorNombre != null)?
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(
onPressed: () => Navigator.pop(context),
child: Text("Cancelar"),
Expanded(
child: SimpleDialogOption(
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';
abstract class PeponetorMensajeFactory {
abstract class PeponatorMensajeFactory {
static PeponatorMensaje mensajeInicial(int inferior, int superior) {
return PeponatorMensaje(
message: "¡Hola! Estoy pensando en un número del $inferior"
......@@ -11,7 +11,7 @@ abstract class PeponetorMensajeFactory {
static PeponatorMensaje respuesta(int intento, int numAdivinar) {
StringBuffer sb = StringBuffer("El $intento está por ");
sb.write(intento < numAdivinar ? "debajo. " : "encima. ");
sb.write("\nSigue intentándolo!");
sb.write("\n¡Sigue intentándolo!");
return PeponatorMensaje(message: sb.toString());
}
......@@ -19,15 +19,15 @@ abstract class PeponetorMensajeFactory {
static PeponatorMensaje victoria(int numIntentos, int numPistas) {
String s1 = numIntentos > 1 ? "s" : "";
String s2 = numPistas > 1 ? "s" : "";
StringBuffer sb = StringBuffer("Felicidades! Lo has conseguido en $numIntentos intento$s1");
sb.write(numPistas == 0 ? "y sin pistas!" : "usando $numPistas pistas$s2");
StringBuffer sb = StringBuffer("¡Felicidades! ¡Lo has conseguido en $numIntentos intento$s1");
sb.write(numPistas == 0 ? " y sin pistas!" : " usando $numPistas pista$s2!");
return PeponatorMensaje(message: sb.toString());
}
static PeponatorMensaje derrota() {
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';
import 'package:peponator/modelo/modelo.dart';
import 'package:peponator/widgets/dificultad_dialog.dart';
// TODO: HACER QUE SEA MENOS FEO
// TODO: MARCAR DIFICULTAD SELECCIONADA
class DificultadWidget extends StatelessWidget {
final bool _staticDif;
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
Widget build(BuildContext context) {
ListaDificultad listaDificultad = Provider.of(context, listen: false);
Dificultad dificultad = listaDificultad.get(indice);
bool seleccionado = indice == listaDificultad.indiceSeleccionado;
Color bg = backgroundColor ?? ((Theme.of(context).brightness == Brightness.light)? Colors.grey.shade300 : Colors.grey.shade800);
return Container(
decoration: seleccionado
? BoxDecoration(border: Border.all(color: Colors.red))
: null,
child: GestureDetector(
final interior = Container(
decoration: seleccionado?
BoxDecoration(
border: Border.all(
color: (Theme.of(context).brightness == Brightness.dark)?
Colors.white : Colors.black,
width: 3.0
)
) : null,
child: InkWell(
splashFactory: InkRipple.splashFactory,
onTap: () => listaDificultad.select(indice),
onLongPress: () async {
onLongPress: (staticDif)? null : () async {
Dificultad? cambiado = await
showDialog<Dificultad>(
context: context,
......@@ -32,27 +48,93 @@ class DificultadWidget extends StatelessWidget {
listaDificultad.update(indice, cambiado);
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
const SizedBox(width: 8, height: 16,),
Text(
dificultad.nombre,
style: Theme.of(context).textTheme.titleLarge,
child: Padding(
padding: EdgeInsets.only(top: (seleccionado)? 13.0 : 16.0, bottom: (seleccionado)? 13.0 : 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: MediaQuery.of(context).size.width/3 - 3,
child: Padding(
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: [
Text("1 - ${dificultad.limite}"),
const SizedBox(width: 8, height: 16,),
],
),
),
secondaryBackground: Container(
color: Colors.red,
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