- Remove duplicate features structure from basura_app - Migrate recycling_guide feature to lib/ - Update main.dart to use new architecture - Clean up old views structure - Remove temporary directories (basura_app, basura_backend, wiki_hackathon)
165 lines
5.5 KiB
Dart
165 lines
5.5 KiB
Dart
// lib/features/recycling_guide/presentation/screens/recycling_guide_screen.dart
|
|
// Pantalla principal — Persona C la agrega al router así:
|
|
// GoRoute(path: '/guia', builder: (_, __) => const RecyclingGuideScreen())
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../providers/recycling_provider.dart';
|
|
import '../widgets/category_card.dart';
|
|
import '../widgets/search_result_tile.dart';
|
|
import 'category_detail_screen.dart';
|
|
|
|
class RecyclingGuideScreen extends ConsumerStatefulWidget {
|
|
const RecyclingGuideScreen({super.key});
|
|
|
|
@override
|
|
ConsumerState<RecyclingGuideScreen> createState() =>
|
|
_RecyclingGuideScreenState();
|
|
}
|
|
|
|
class _RecyclingGuideScreenState extends ConsumerState<RecyclingGuideScreen> {
|
|
final _searchCtrl = TextEditingController();
|
|
bool _buscando = false;
|
|
|
|
@override
|
|
void dispose() {
|
|
_searchCtrl.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _onSearchChanged(String query) {
|
|
setState(() => _buscando = query.trim().isNotEmpty);
|
|
ref.read(recyclingSearchProvider.notifier).buscar(query);
|
|
}
|
|
|
|
void _limpiarBusqueda() {
|
|
_searchCtrl.clear();
|
|
setState(() => _buscando = false);
|
|
ref.read(recyclingSearchProvider.notifier).limpiar();
|
|
FocusScope.of(context).unfocus();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Guía de separación'),
|
|
actions: [
|
|
// Badge offline — refuerza que funciona sin internet
|
|
Container(
|
|
margin: const EdgeInsets.only(right: 12),
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: Colors.green.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: const Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.offline_bolt, size: 14, color: Colors.white),
|
|
SizedBox(width: 4),
|
|
Text(
|
|
'sin internet',
|
|
style: TextStyle(fontSize: 11, color: Colors.white),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
body: Column(
|
|
children: [
|
|
// ── Buscador ────────────────────────────────────────────
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
|
child: TextField(
|
|
controller: _searchCtrl,
|
|
onChanged: _onSearchChanged,
|
|
decoration: InputDecoration(
|
|
hintText: '¿Dónde va el aceite? ¿y la pila?',
|
|
prefixIcon: const Icon(Icons.search),
|
|
suffixIcon: _buscando
|
|
? IconButton(
|
|
icon: const Icon(Icons.close),
|
|
onPressed: _limpiarBusqueda,
|
|
)
|
|
: null,
|
|
),
|
|
),
|
|
),
|
|
|
|
// ── Contenido dinámico ──────────────────────────────────
|
|
Expanded(
|
|
child: _buscando
|
|
? _SearchResults()
|
|
: _CategoryList(),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// ── Lista de categorías ──────────────────────────────────────────────
|
|
|
|
class _CategoryList extends ConsumerWidget {
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final async = ref.watch(recyclingCategoriesProvider);
|
|
|
|
return async.when(
|
|
loading: () => const Center(child: CircularProgressIndicator()),
|
|
error: (e, _) => Center(child: Text('Error: $e')),
|
|
data: (categorias) => ListView.separated(
|
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
|
|
itemCount: categorias.length,
|
|
separatorBuilder: (_, __) => const SizedBox(height: 12),
|
|
itemBuilder: (context, i) => CategoryCard(
|
|
categoria: categorias[i],
|
|
onTap: () => Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (_) =>
|
|
CategoryDetailScreen(categoria: categorias[i]),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// ── Resultados de búsqueda ───────────────────────────────────────────
|
|
|
|
class _SearchResults extends ConsumerWidget {
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final estado = ref.watch(recyclingSearchProvider);
|
|
|
|
return switch (estado) {
|
|
_Idle() => const SizedBox.shrink(),
|
|
_Loading() => const Center(child: CircularProgressIndicator()),
|
|
_Done(results: final r) when r.isEmpty => Center(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.search_off, size: 48, color: Colors.grey[400]),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
'No encontramos ese residuo.',
|
|
style: TextStyle(color: Colors.grey[600]),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
_Done(results: final r) => ListView.builder(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
itemCount: r.length,
|
|
itemBuilder: (_, i) => SearchResultTile(resultado: r[i]),
|
|
),
|
|
_ => const SizedBox.shrink(),
|
|
};
|
|
}
|
|
}
|