Skip to content

Commit f257984

Browse files
xsilvamoIm-Fran
andauthored
fix: adaptive button agregar notas in horario (#51)
* fix: adaptive button agregar notas in horario * fix: color fix * feat: add intersperse extension for lists and refactor notas widget for better readability * fix: correct index usage in FilaNota for grade and percentage text fields --------- Co-authored-by: Francisco Solis <30329003+Im-Fran@users.noreply.github.com>
1 parent ed407b9 commit f257984

4 files changed

Lines changed: 152 additions & 78 deletions

File tree

lib/core/utils/utilities.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ extension RotateList<T> on List<T> {
4343
List<T> rotate(int count) => count > 0 ? (sublist(count)..addAll(sublist(0, count))) : (sublist(length + count)..addAll(sublist(0, length + count)));
4444
}
4545

46+
/// Esta extensión permite insertar un elemento entre cada elemento de la lista, excepto al final.
47+
/// Ejemplo:
48+
/// ```dart
49+
/// List<String> lista = ["a", "b", "c"];
50+
/// print(lista.intersperse("-")); // ["a", "-", "b", "-", "c"]
51+
/// ```
52+
extension IntersperseList<T> on List<T> {
53+
List<T> intersperse(T element) => length > 1 ? expand((e) sync* {
54+
yield e;
55+
if (e != last) yield element;
56+
}).toList() : this;
57+
}
58+
4659
/// Capitaliza el texto entregado
4760
/// Ejemplo:
4861
/// ```dart
Lines changed: 59 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1+
import "package:collection/collection.dart";
12
import "package:flutter/material.dart";
2-
import "package:flutter/services.dart";
33
import "package:get/get.dart";
44
import "package:miutem/core/models/evaluacion/evaluacion.dart";
55
import "package:miutem/core/services/controllers/notas_controller.dart";
66
import "package:miutem/core/utils/utils.dart";
7+
import "package:miutem/screens/notas/widgets/notas/fila_nota.dart";
78
import "package:miutem/styles/styles.dart";
89

910
class Notas extends StatelessWidget {
10-
1111
final bool canAddNotas;
1212
final NotasController notasController;
1313

@@ -20,89 +20,70 @@ class Notas extends StatelessWidget {
2020
@override
2121
Widget build(BuildContext context) => Column(
2222
crossAxisAlignment: CrossAxisAlignment.start,
23-
mainAxisSize: MainAxisSize.max,
2423
children: [
2524
Text("Notas", style: Theme.of(context).textTheme.bodyMedium),
26-
const SizedBox(height: 12),
27-
SizedBox(
28-
width: double.infinity,
29-
child: Card(
30-
color: Theme.of(context).scaffoldBackgroundColor,
31-
margin: EdgeInsets.zero,
32-
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10), side: BorderSide(color: AppTheme.lightGrey)),
33-
child: Padding(
34-
padding: const EdgeInsets.all(20),
35-
child: Column(
36-
mainAxisSize: MainAxisSize.min,
37-
mainAxisAlignment: MainAxisAlignment.center,
38-
crossAxisAlignment: CrossAxisAlignment.center,
39-
children: [
40-
Obx(() => GridView(
41-
shrinkWrap: true,
42-
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
43-
crossAxisCount: 3,
44-
childAspectRatio: 3,
45-
mainAxisSpacing: 12,
46-
crossAxisSpacing: 12,
47-
),
25+
Space.small,
26+
Card(
27+
margin: EdgeInsets.zero,
28+
color: Theme.of(context).scaffoldBackgroundColor,
29+
shape: RoundedRectangleBorder(
30+
borderRadius: BorderRadius.circular(10),
31+
side: BorderSide(color: AppTheme.lightGrey),
32+
),
33+
child: Padding(
34+
padding: const EdgeInsets.all(20),
35+
child: Column(
36+
mainAxisSize: MainAxisSize.min,
37+
children: [
38+
// Usamos Obx solo para la parte que cambia
39+
Obx(
40+
() => Column(
4841
children: [
49-
const Center(child: Text("Notas")),
50-
const Center(child: Text("Porcentaje")),
51-
const SizedBox.shrink(),
52-
for (int i = 0; i < notasController.percentageTextFieldControllers.length; i++) ...[
53-
_buildTextField(context: context, enabled: true, controller: notasController.gradeTextFieldControllers[i], textInputAction: TextInputAction.next, hintText: formatoNota(notasController.suggestedGrade), formatters: [notaInputFormatter], onChanged: (value) {
54-
final grade = notasController.partialGrades[i];
55-
grade.nota = double.tryParse(value.replaceAll(",", "."));
56-
notasController.updateGradeAt(i, grade);
57-
}),
58-
_buildTextField(context: context,enabled: true, controller: notasController.percentageTextFieldControllers[i], textInputAction: TextInputAction.done, hintText: notasController.suggestedPercentage?.toStringAsFixed(0) ?? "--", onChanged: (value) {
59-
final grade = notasController.partialGrades[i];
60-
grade.porcentaje = double.tryParse(value.replaceAll(",", ".")) ?? 0;
61-
notasController.updateGradeAt(i, grade);
62-
}),
63-
IconButton(onPressed: () => notasController.removeGradeAt(i), icon: const Icon(AppIcons.delete, size: 20)),
64-
],
65-
const SizedBox.shrink(),
66-
SizedBox.expand(child: Center(child: FilledButton.tonalIcon(icon: const Icon(AppIcons.add), label: const Text("Nota"), onPressed: () {
67-
if(!canAddNotas) {
68-
showErrorSnackbar(context, "Las notas están cargando... Intenta más tarde.");
69-
return;
70-
}
42+
// Encabezados manuales para evitar el GridView rígido
43+
const Row(
44+
mainAxisAlignment: MainAxisAlignment.center,
45+
children: [
46+
Expanded(child: Center(child: Text("Notas"))),
47+
HorizontalSpace.small,
48+
Expanded(child: Center(child: Text("Porcentaje"))),
49+
HorizontalSpace.extraExtraLarge,
50+
],
51+
),
52+
HorizontalSpace.small,
53+
54+
// Filas de Notas
55+
...notasController.percentageTextFieldControllers
56+
.mapIndexed<Widget>(
57+
(i, controller) => FilaNota(
58+
notasController: notasController,
59+
index: i,
60+
),
61+
)
62+
.toList()
63+
.intersperse(Space.small),
7164

72-
notasController.addGrade(IEvaluacion());
73-
}))),
74-
const SizedBox.shrink(),
65+
Space.extraSmall,
66+
FilledButton.tonalIcon(
67+
onPressed: () {
68+
if (!canAddNotas) {
69+
showErrorSnackbar(
70+
context,
71+
"Las notas están cargando... Intenta más tarde.",
72+
);
73+
return;
74+
}
75+
notasController.addGrade(IEvaluacion());
76+
},
77+
icon: const Icon(AppIcons.add),
78+
label: const Text("Agregar Nota"),
79+
),
7580
],
76-
)),
77-
],
78-
),
81+
),
82+
),
83+
],
7984
),
8085
),
8186
),
8287
],
8388
);
84-
85-
Widget _buildTextField({
86-
required BuildContext context,
87-
required bool enabled,
88-
required TextEditingController controller,
89-
required TextInputAction textInputAction,
90-
required String? hintText,
91-
Function(String)? onChanged,
92-
List<TextInputFormatter>? formatters,
93-
}) => TextField(
94-
enabled: enabled,
95-
controller: controller,
96-
style: Theme.of(context).textTheme.bodyMedium,
97-
decoration: InputDecoration(
98-
hintText: hintText ?? "--",
99-
filled: true,
100-
),
101-
textAlign: TextAlign.center,
102-
textAlignVertical: TextAlignVertical.center,
103-
onChanged: onChanged,
104-
textInputAction: textInputAction,
105-
inputFormatters: formatters,
106-
107-
);
108-
}
89+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import "package:flutter/material.dart";
2+
import "package:flutter/services.dart";
3+
import "package:miutem/core/services/controllers/notas_controller.dart";
4+
import "package:miutem/core/utils/utils.dart";
5+
import "package:miutem/styles/styles.dart";
6+
7+
class FilaNota extends StatelessWidget {
8+
final NotasController notasController;
9+
final int index;
10+
11+
const FilaNota({
12+
super.key,
13+
required this.notasController,
14+
required this.index,
15+
});
16+
17+
@override
18+
Widget build(BuildContext context) => Row(
19+
children: [
20+
Expanded(
21+
child: _buildTextField(
22+
context: context,
23+
enabled: true,
24+
controller: notasController.gradeTextFieldControllers[index],
25+
textInputAction: TextInputAction.next,
26+
hintText: formatoNota(notasController.suggestedGrade),
27+
formatters: [notaInputFormatter],
28+
onChanged: (value) {
29+
final grade = notasController.partialGrades[index];
30+
grade.nota = double.tryParse(value.replaceAll(",", "."));
31+
notasController.updateGradeAt(index, grade);
32+
},
33+
),
34+
),
35+
HorizontalSpace.small,
36+
Expanded(
37+
child: _buildTextField(
38+
context: context,
39+
enabled: true,
40+
controller: notasController.percentageTextFieldControllers[index],
41+
textInputAction: TextInputAction.done,
42+
hintText:
43+
notasController.suggestedPercentage?.toStringAsFixed(0) ?? "--",
44+
onChanged: (value) {
45+
final grade = notasController.partialGrades[index];
46+
grade.porcentaje = double.tryParse(value.replaceAll(",", ".")) ?? 0;
47+
notasController.updateGradeAt(index, grade);
48+
},
49+
),
50+
),
51+
IconButton(
52+
onPressed: () => notasController.removeGradeAt(index),
53+
icon: const Icon(AppIcons.delete, size: 20),
54+
color: Theme.of(context).textTheme.bodyMedium?.color,
55+
),
56+
],
57+
);
58+
59+
Widget _buildTextField({
60+
required BuildContext context,
61+
required bool enabled,
62+
required TextEditingController controller,
63+
required TextInputAction textInputAction,
64+
required String? hintText,
65+
Function(String)? onChanged,
66+
List<TextInputFormatter>? formatters,
67+
}) => TextField(
68+
enabled: enabled,
69+
controller: controller,
70+
style: Theme.of(context).textTheme.bodyMedium,
71+
decoration: InputDecoration(hintText: hintText ?? "--", filled: true),
72+
textAlign: TextAlign.center,
73+
textAlignVertical: TextAlignVertical.center,
74+
onChanged: onChanged,
75+
textInputAction: textInputAction,
76+
inputFormatters: formatters,
77+
);
78+
}

lib/styles/theme/space.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class Space {
1414
}
1515

1616
class HorizontalSpace {
17+
/// 48 PX para separar secciones muy distintas o elementos muy importantes
18+
static const Widget extraExtraLarge = SizedBox(width: 48);
1719
/// 28 PX para separar secciones muy distintas o elementos muy importantes
1820
static const Widget extraLarge = SizedBox(width: 28);
1921
/// 20 PX para separar elementos muy distintos o secciones

0 commit comments

Comments
 (0)