Files
farolero/lib/pantallas/pantalla_notas.dart
T
FreeTLab ae0d55c00f
Build & Deploy Farolero / Análisis de código (push) Successful in 14s
Build & Deploy Farolero / Build APK + AAB release (push) Successful in 1m16s
fix(android): target API level 35 as required by Google Play
Play rejected version code 54 for still targeting 34.

From API 35 Android forces edge-to-edge: content is drawn behind the system
bars and it is up to each screen to keep clear of them. Seven screens had no
SafeArea and their bottom action button would have ended up under the
gesture bar, so FondoFarolero gains a respetarBarras flag that insets the
content while the background still paints full bleed. The seventeen screens
that already handle it are untouched.

The navigation bar colour is ignored under edge-to-edge, so its icons are
now forced light to stay visible over the dark background.

Verified on the merged manifest: targetSdkVersion 35, and the release bundle
builds. The visual result has not been checked on a device.
2026-07-25 22:27:28 +02:00

237 lines
7.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:farolero/l10n/generated/app_localizations.dart';
import 'package:provider/provider.dart';
import '../estado/estado_juego.dart';
import '../servicios/servicio_notas.dart';
import '../tema/componentes_farolero.dart';
import '../tema/tema_app.dart';
class PantallaNotas extends StatefulWidget {
const PantallaNotas({super.key});
@override
State<PantallaNotas> createState() => _PantallaNotasState();
}
class _PantallaNotasState extends State<PantallaNotas> {
String? _jugadorSeleccionadoId;
final Map<String, TextEditingController> _controladores = {};
final _controladorNotaLibre = TextEditingController();
bool _cargado = false;
@override
void dispose() {
for (final c in _controladores.values) {
c.dispose();
}
_controladorNotaLibre.dispose();
super.dispose();
}
Future<void> _cargarNotas(String jugadorId) async {
final datos = await ServicioNotas.cargarNotas(jugadorId);
final notas = datos['notas'] as Map<String, String>;
final notaLibre = datos['notaLibre'] as String;
for (final entrada in notas.entries) {
if (_controladores.containsKey(entrada.key)) {
_controladores[entrada.key]!.text = entrada.value;
}
}
_controladorNotaLibre.text = notaLibre;
setState(() => _cargado = true);
}
Future<void> _guardarNotas() async {
if (_jugadorSeleccionadoId == null) return;
final notas = <String, String>{};
for (final entrada in _controladores.entries) {
if (entrada.value.text.isNotEmpty) {
notas[entrada.key] = entrada.value.text;
}
}
await ServicioNotas.guardarNotas(
_jugadorSeleccionadoId!,
notas,
_controladorNotaLibre.text,
);
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final estado = context.watch<EstadoJuego>();
final partida = estado.partida;
if (partida == null) return const SizedBox.shrink();
// Inicializar controladores
for (final j in partida.jugadores) {
_controladores.putIfAbsent(j.id, () => TextEditingController());
}
return Scaffold(
appBar: AppBar(
title: Text(l10n.notesTitle),
actions: [
if (_jugadorSeleccionadoId != null)
IconButton(
icon: IconoFarolero(Icons.save),
onPressed: () async {
await _guardarNotas();
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(l10n.notesSaved)),
);
}
},
),
],
),
body: FondoFarolero(
intenso: true,
respetarBarras: true,
child: _jugadorSeleccionadoId == null
? _construirSelectorJugador(partida)
: _construirNotas(partida),
),
);
}
Widget _construirSelectorJugador(dynamic partida) {
final l10n = AppLocalizations.of(context)!;
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const ArteGameplayFarolero.notas(height: 138),
const SizedBox(height: 10),
Text(
l10n.whoAreYou,
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 8),
Text(
l10n.selectYourName,
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 16),
Expanded(
child: ListView.builder(
itemCount: partida.jugadoresActivos.length,
itemBuilder: (context, index) {
final j = partida.jugadoresActivos[index];
return Card(
child: ListTile(
leading: CircleAvatar(
backgroundColor: TemaApp.colorAcento,
child: Text('${index + 1}',
style: const TextStyle(color: Colors.white)),
),
title: Text(j.nombre),
trailing: IconoFarolero(Icons.arrow_forward_ios, size: 16),
onTap: () {
setState(() {
_jugadorSeleccionadoId = j.id;
_cargado = false;
});
_cargarNotas(j.id);
},
),
);
},
),
),
],
),
);
}
Widget _construirNotas(dynamic partida) {
if (!_cargado) {
return const Center(child: CircularProgressIndicator());
}
final l10n = AppLocalizations.of(context)!;
final jugadorActual = partida.jugadores
.firstWhere((j) => j.id == _jugadorSeleccionadoId);
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const ArteGameplayFarolero.notas(height: 120),
const SizedBox(height: 8),
Row(
children: [
IconButton(
icon: IconoFarolero(Icons.arrow_back),
onPressed: () async {
await _guardarNotas();
setState(() {
_jugadorSeleccionadoId = null;
_cargado = false;
for (final c in _controladores.values) {
c.clear();
}
_controladorNotaLibre.clear();
});
},
),
Text(
l10n.notesOf(jugadorActual.nombre),
style: Theme.of(context).textTheme.titleLarge,
),
],
),
const SizedBox(height: 16),
// Notas por jugador
Text(
l10n.notesAboutPlayers,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: TemaApp.colorTextoSecundario,
),
),
const SizedBox(height: 8),
...partida.jugadoresActivos.where((j) => j.id != _jugadorSeleccionadoId).map<Widget>((j) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: TextField(
controller: _controladores[j.id],
decoration: InputDecoration(
labelText: j.nombre,
prefixIcon: IconoFarolero(Icons.person, size: 20),
hintText: l10n.playerNoteHint,
),
maxLines: 2,
minLines: 1,
),
);
}),
const SizedBox(height: 16),
Text(
l10n.freeNote,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: TemaApp.colorTextoSecundario,
),
),
const SizedBox(height: 8),
TextField(
controller: _controladorNotaLibre,
decoration: InputDecoration(
hintText: l10n.freeNoteHint,
prefixIcon: IconoFarolero(Icons.note, size: 20),
),
maxLines: 5,
minLines: 3,
),
],
),
);
}
}