fix(eq): seed device ID at startup and add device management UI
Fix multi-device EQ auto-switching by calling obtenerDispositivoActual() during cargarPersistido() to seed the initial device ID. Add device management modal with rename support, EQ preset editing, and connection status indicator. Translate device UI keys to all 13 locales.
This commit is contained in:
@@ -16,6 +16,7 @@ import '../l10n/display_names.dart';
|
||||
import '../l10n/gen/app_localizations.dart';
|
||||
import '../modelos/emisora.dart';
|
||||
import '../modelos/grupo_favoritos.dart';
|
||||
import '../modelos/preset_ecualizador.dart';
|
||||
import '../widgets/ecualizador_widget.dart';
|
||||
import '../widgets/pluri_glass_surface.dart';
|
||||
import '../widgets/pluri_icon.dart';
|
||||
@@ -739,33 +740,9 @@ class _SeccionEcualizadorAvanzado extends StatelessWidget {
|
||||
)
|
||||
else
|
||||
for (final entry in presetsDispositivo.entries)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.headphones_rounded, size: 20),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
entry.key,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
Text(
|
||||
l10n.advancedEqDevicePresetLabel(
|
||||
entry.value.nombre,
|
||||
),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
_FilaDispositivo(
|
||||
deviceId: entry.key,
|
||||
preset: entry.value,
|
||||
),
|
||||
],
|
||||
],
|
||||
@@ -774,6 +751,162 @@ class _SeccionEcualizadorAvanzado extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// A single device row in the known-devices list.
|
||||
///
|
||||
/// Shows a connection indicator (green dot) when [deviceId] matches the
|
||||
/// currently active device. Tapping the edit icon opens [_DialogoEdicionDispositivo].
|
||||
class _FilaDispositivo extends StatelessWidget {
|
||||
const _FilaDispositivo({required this.deviceId, required this.preset});
|
||||
|
||||
final String deviceId;
|
||||
final PresetEcualizador preset;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final eq = context.watch<EstadoEcualizador>();
|
||||
final isActive = eq.dispositivoActualId == deviceId;
|
||||
final displayName = eq.nombreVisible(deviceId, '');
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
if (isActive)
|
||||
const Icon(Icons.circle, size: 10, color: Colors.green)
|
||||
else
|
||||
const SizedBox(width: 10),
|
||||
const SizedBox(width: 8),
|
||||
const Icon(Icons.headphones_rounded, size: 20),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
displayName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
Text(
|
||||
l10n.advancedEqDevicePresetLabel(preset.nombre),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit_rounded, size: 20),
|
||||
tooltip: l10n.eqDeviceEditTitle,
|
||||
onPressed: () => _abrirModal(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _abrirModal(BuildContext context) async {
|
||||
await showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
showDragHandle: true,
|
||||
builder: (ctx) => _DialogoEdicionDispositivo(
|
||||
deviceId: deviceId,
|
||||
preset: preset,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Bottom sheet for editing a device's custom name and EQ preset.
|
||||
class _DialogoEdicionDispositivo extends StatefulWidget {
|
||||
const _DialogoEdicionDispositivo({
|
||||
required this.deviceId,
|
||||
required this.preset,
|
||||
});
|
||||
|
||||
final String deviceId;
|
||||
final PresetEcualizador preset;
|
||||
|
||||
@override
|
||||
State<_DialogoEdicionDispositivo> createState() =>
|
||||
_DialogoEdicionDispositivoState();
|
||||
}
|
||||
|
||||
class _DialogoEdicionDispositivoState
|
||||
extends State<_DialogoEdicionDispositivo> {
|
||||
late final TextEditingController _nombreCtrl;
|
||||
late PresetEcualizador _presetActual;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final eq = context.read<EstadoEcualizador>();
|
||||
final displayName = eq.nombreVisible(widget.deviceId, '');
|
||||
_nombreCtrl = TextEditingController(text: displayName);
|
||||
_presetActual = widget.preset;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nombreCtrl.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _guardar() async {
|
||||
final eq = context.read<EstadoEcualizador>();
|
||||
await eq.renombrarDispositivo(widget.deviceId, _nombreCtrl.text);
|
||||
if (_presetActual != widget.preset) {
|
||||
await eq.guardarPresetDispositivo(widget.deviceId, _presetActual);
|
||||
}
|
||||
if (mounted) Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final bottom = MediaQuery.viewInsetsOf(context).bottom;
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(20, 0, 20, bottom + 24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.eqDeviceEditTitle,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _nombreCtrl,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.eqDeviceNameLabel,
|
||||
hintText: l10n.eqDeviceNameHint,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
EcualizadorWidget(
|
||||
preset: _presetActual,
|
||||
onCambio: (p) => setState(() => _presetActual = p),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
FilledButton.icon(
|
||||
onPressed: _guardar,
|
||||
icon: const Icon(Icons.save_rounded),
|
||||
label: Text(l10n.eqDeviceNameConfirm),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SeccionOrdenListas extends StatelessWidget {
|
||||
const _SeccionOrdenListas();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user