130 lines
4.3 KiB
Dart
130 lines
4.3 KiB
Dart
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),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|