feat(mvp): PluriWave Fase 1 — estructura completa de la app
Some checks failed
Flutter CI/CD — PluriWave / Test + Build (pull_request) Has been cancelled
Some checks failed
Flutter CI/CD — PluriWave / Test + Build (pull_request) Has been cancelled
- Modelo Emisora: campos completos Radio Browser API (fromApi + fromMap) - ServicioRadio: cliente Radio Browser API (populares, tendencias, buscar por nombre/país/idioma/tag) - ServicioAudio: just_audio + audio_service wrapper (play/pause/stop/toggle, fade, background handler) - ServicioTimer: countdown con fade out gradual (15/30/60/90 min) - ServicioFavoritos: actualizado a v2 con campos codec/bitrate/votes/clickcount - EstadoRadio: ChangeNotifier global con Provider - PantallaInicio: grid emisoras populares, chips género, shimmer loading, pull-to-refresh - PantallaBuscar: SearchBar + filtros país/idioma, lista resultados - PantallaFavoritos: ReorderableListView + swipe-to-delete (Dismissible) - TarjetaEmisora: card + modo compacto ListTile, cached_network_image, shimmer fallback - MiniReproductor: barra inferior persistente con stream de estado - app.dart: MaterialApp + Provider + NavigationBar + timer dialog - main.dart: punto de entrada limpio - AndroidManifest.xml: permisos INTERNET + FOREGROUND_SERVICE + audio_service receivers
This commit is contained in:
136
lib/estado/estado_radio.dart
Normal file
136
lib/estado/estado_radio.dart
Normal file
@@ -0,0 +1,136 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../modelos/emisora.dart';
|
||||
import '../servicios/servicio_audio.dart';
|
||||
import '../servicios/servicio_favoritos.dart';
|
||||
import '../servicios/servicio_radio.dart';
|
||||
import '../servicios/servicio_timer.dart';
|
||||
|
||||
/// Estado global de la app con ChangeNotifier (Provider).
|
||||
///
|
||||
/// Centraliza: reproductoor, favoritos, búsqueda, timer.
|
||||
class EstadoRadio extends ChangeNotifier {
|
||||
final ServicioAudio audio = ServicioAudio();
|
||||
final ServicioFavoritos favoritos = ServicioFavoritos();
|
||||
final ServicioRadio radio = ServicioRadio();
|
||||
late final ServicioTimer timer;
|
||||
|
||||
List<Emisora> _populares = [];
|
||||
List<Emisora> _tendencias = [];
|
||||
List<Emisora> _resultadosBusqueda = [];
|
||||
List<Emisora> _listafavoritos = [];
|
||||
|
||||
bool _cargandoPopulares = false;
|
||||
bool _cargandoBusqueda = false;
|
||||
String? _error;
|
||||
|
||||
EstadoRadio() {
|
||||
timer = ServicioTimer(audio);
|
||||
_init();
|
||||
}
|
||||
|
||||
List<Emisora> get populares => _populares;
|
||||
List<Emisora> get tendencias => _tendencias;
|
||||
List<Emisora> get resultadosBusqueda => _resultadosBusqueda;
|
||||
List<Emisora> get listaFavoritos => _listafavoritos;
|
||||
bool get cargandoPopulares => _cargandoPopulares;
|
||||
bool get cargandoBusqueda => _cargandoBusqueda;
|
||||
String? get error => _error;
|
||||
Emisora? get emisoraActual => audio.emisoraActual;
|
||||
Stream<EstadoReproduccion> get estadoStream => audio.estadoStream;
|
||||
|
||||
Future<void> _init() async {
|
||||
await Future.wait([
|
||||
cargarPopulares(),
|
||||
cargarFavoritos(),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> cargarPopulares() async {
|
||||
_cargandoPopulares = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final results = await Future.wait([
|
||||
radio.obtenerPopulares(limit: 30),
|
||||
radio.obtenerTendencias(limit: 20),
|
||||
]);
|
||||
_populares = results[0];
|
||||
_tendencias = results[1];
|
||||
} catch (e) {
|
||||
_error = 'Error al cargar emisoras: $e';
|
||||
} finally {
|
||||
_cargandoPopulares = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> cargarFavoritos() async {
|
||||
_listafavoritos = await favoritos.obtenerTodos();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> buscar({
|
||||
String? nombre,
|
||||
String? pais,
|
||||
String? idioma,
|
||||
String? tag,
|
||||
}) async {
|
||||
_cargandoBusqueda = true;
|
||||
_resultadosBusqueda = [];
|
||||
notifyListeners();
|
||||
try {
|
||||
_resultadosBusqueda = await radio.buscar(
|
||||
nombre: nombre,
|
||||
pais: pais,
|
||||
idioma: idioma,
|
||||
tag: tag,
|
||||
);
|
||||
} catch (e) {
|
||||
_error = 'Error en búsqueda: $e';
|
||||
} finally {
|
||||
_cargandoBusqueda = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reproducir(Emisora emisora) async {
|
||||
try {
|
||||
await audio.reproducir(emisora);
|
||||
radio.registrarClick(emisora.uuid); // fire & forget
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
_error = 'No se puede reproducir esta emisora';
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> togglePlay() async {
|
||||
await audio.togglePlay();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<bool> toggleFavorito(Emisora emisora) async {
|
||||
final esFav = await favoritos.toggleFavorito(emisora);
|
||||
await cargarFavoritos();
|
||||
return esFav;
|
||||
}
|
||||
|
||||
Future<bool> esFavorito(String uuid) => favoritos.esFavorito(uuid);
|
||||
|
||||
void iniciarTimer(int minutos) {
|
||||
timer.iniciar(minutos);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void cancelarTimer() {
|
||||
timer.cancelar();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
audio.dispose();
|
||||
timer.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user