refactor(ajustes): split remaining Settings sections into pushed screens

This commit is contained in:
2026-07-28 22:14:03 +02:00
parent 48ece948ff
commit c1903623be
29 changed files with 1538 additions and 735 deletions
@@ -0,0 +1,129 @@
import 'package:flutter/material.dart';
import '../../l10n/gen/app_localizations.dart';
import '../../servicios/musica_local_auto.dart';
import '../../widgets/pluri_glass_surface.dart';
import '../../widgets/pluri_layout.dart';
import '../../widgets/pluri_push_scaffold.dart';
/// GRABACIONES Y MÚSICA group · "Música local" (design ADR-3). Body moved
/// verbatim from the former `_SeccionMusicaLocal` in `pantalla_ajustes.dart`
/// — only the panel header's icon and title were removed (the pushed
/// screen's title now carries them). Deliberately does NOT use
/// `FilePicker.platform` (see tasks.md "Grounding corrections") —
/// [FuenteMusicaLocalAutoImpl.elegirCarpeta] calls the native
/// `pickMusicFolder` channel method directly, since it needs a
/// persistable-grant SAF tree URI, not a plain filesystem path.
class PantallaAjustesMusicaLocal extends StatelessWidget {
const PantallaAjustesMusicaLocal({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
return PluriPushScaffold(
title: l10n.localMusicSectionTitle,
body: ListView(
padding: PluriLayout.pageContentPadding,
children: const [_CuerpoMusicaLocal()],
),
);
}
}
class _CuerpoMusicaLocal extends StatefulWidget {
const _CuerpoMusicaLocal();
@override
State<_CuerpoMusicaLocal> createState() => _CuerpoMusicaLocalState();
}
class _CuerpoMusicaLocalState extends State<_CuerpoMusicaLocal> {
final _fuente = FuenteMusicaLocalAutoImpl();
late Future<String?> _carpetaActual;
@override
void initState() {
super.initState();
_carpetaActual = _fuente.carpetaActual();
}
Future<void> _elegirCarpeta(BuildContext context) async {
final messenger = ScaffoldMessenger.of(context);
final l10n = AppLocalizations.of(context);
try {
final uri = await _fuente.elegirCarpeta();
if (uri == null) return; // Cancelled — no snackbar, matches the SAF
// picker's own "nothing changed" affordance.
if (!context.mounted) return;
setState(() {
_carpetaActual = Future.value(uri);
});
messenger.showSnackBar(
SnackBar(content: Text(l10n.localMusicFolderUpdated)),
);
} catch (e) {
if (!context.mounted) return;
messenger.showSnackBar(
SnackBar(content: Text(l10n.localMusicFolderSaveError(e.toString()))),
);
}
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
return PluriGlassSurface(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 8),
Text(
l10n.localMusicSectionDescription,
style: Theme.of(context).textTheme.bodySmall,
),
FutureBuilder<String?>(
future: _carpetaActual,
builder: (ctx, snap) {
final carpeta = snap.data;
return ListTile(
contentPadding: EdgeInsets.zero,
leading: const Icon(Icons.folder_outlined),
title: Text(l10n.localMusicFolderTitle),
subtitle: Text(
(carpeta == null || carpeta.isEmpty)
? l10n.localMusicFolderNotConfigured
: nombreCarpetaDesdeUri(
carpeta,
nombreGenerico: l10n.localMusicFolderGenericName,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
);
},
),
const SizedBox(height: 8),
FutureBuilder<String?>(
future: _carpetaActual,
builder: (ctx, snap) {
final configurada = (snap.data ?? '').isNotEmpty;
return Align(
alignment: Alignment.centerLeft,
child: OutlinedButton.icon(
icon: const Icon(Icons.folder_open_rounded),
label: Text(
configurada
? l10n.localMusicChangePath
: l10n.localMusicChoosePath,
),
onPressed: () => _elegirCarpeta(context),
),
);
},
),
],
),
);
}
}