123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- import 'dart:convert';
- import 'package:flutter/foundation.dart';
- import 'package:flutter/material.dart';
- import 'package:http/http.dart' as http;
- import 'package:shared_preferences/shared_preferences.dart';
- import 'custom_info_card.dart';
- import 'favorites_page.dart';
-
- void main() => runApp(const MyApp());
-
- class MyApp extends StatelessWidget {
- const MyApp({super.key});
-
- static const Color primaryColor = Color.fromARGB(255, 254, 100, 91);
- static const Color secondaryColor = Colors.orange;
-
- @override
- Widget build(BuildContext context) => MaterialApp(
- title: 'UPR App',
- theme: ThemeData(
- primaryColor: primaryColor,
- appBarTheme: const AppBarTheme(
- backgroundColor: primaryColor,
- elevation: 0,
- ),
- bottomNavigationBarTheme: const BottomNavigationBarThemeData(
- selectedItemColor: Colors.white,
- unselectedItemColor: Colors.white70,
- backgroundColor: primaryColor,
- type: BottomNavigationBarType.fixed,
- showSelectedLabels: true,
- showUnselectedLabels: true,
- ),
- ),
- home: const HomePage(),
- debugShowCheckedModeBanner: false,
- );
- }
-
- class HomePage extends StatefulWidget {
- const HomePage({super.key});
-
- @override
- State<HomePage> createState() => _HomePageState();
- }
-
- class _HomePageState extends State<HomePage> {
- List<Map<String, dynamic>> allCards = [];
- List<Map<String, dynamic>> filteredCards = [];
- List<Map<String, dynamic>> favoriteCards = [];
- int _currentIndex = 0;
- bool _isLoading = false;
- static const String _sheetUrl = 'https://script.google.com/macros/s/AKfycbw1htmk7rlwLMOkOrpZ9ED-7ErWgMlYNtLKxoQ9QO-FopqTAhJuQkR7Gs1LxTJakbMT/exec';
-
- @override
- void initState() {
- super.initState();
- _loadData();
- }
-
- Future<void> _loadData() async {
- final prefs = await SharedPreferences.getInstance();
- setState(() {
- allCards = prefs.getString('infoCardsData') != null
- ? List<Map<String, dynamic>>.from(json.decode(prefs.getString('infoCardsData')!))
- : [_defaultCard()];
- filteredCards = allCards;
- });
- await _fetchData();
- }
-
- Future<void> _fetchData() async {
- try {
- setState(() => _isLoading = true);
- final response = await http.get(Uri.parse(_sheetUrl)).timeout(const Duration(seconds: 10));
- if (response.statusCode == 200) {
- final newData = List<Map<String, dynamic>>.from(json.decode(response.body));
- if (json.encode(newData) != json.encode(allCards)) {
- setState(() {
- allCards = newData;
- filteredCards = _filterCardsByCategory(_currentIndex);
- });
- (await SharedPreferences.getInstance()).setString('infoCardsData', response.body);
- }
- }
- } catch (e) {
- if (kDebugMode) print('Fetch error: $e');
- } finally {
- if (mounted) setState(() => _isLoading = false);
- }
- }
-
- Future<void> _loadFavorites() async {
- final prefs = await SharedPreferences.getInstance();
- final favKeys = prefs.getKeys().where((k) => k.startsWith('fav_') && (prefs.getBool(k) ?? false));
- setState(() => favoriteCards = allCards.where((c) => favKeys.contains('fav_${c['title']}')).toList());
- }
-
- List<Map<String, dynamic>> _filterCardsByCategory(int index) {
- switch (index) {
- case 0: return allCards;
- case 1: return allCards.where((card) => card['categoria'] == 'Actividades').toList();
- case 2: return allCards.where((card) => card['categoria'] == 'Noticias UPR').toList();
- case 3: return allCards.where((card) => card['categoria'] == 'Fechas Importantes').toList();
- case 4: return favoriteCards;
- default: return allCards;
- }
- }
-
- Map<String, dynamic> _defaultCard() => {
- 'image': 'assets/icons/icono1.jpg',
- 'title': 'Tutorias de programación',
- 'subtitle': '8:00am a 11:30am En biblioteca Lázaro',
- 'texto': 'Detalles completos sobre tutorías...',
- 'categoria': 'Actividades',
- };
-
- @override
- Widget build(BuildContext context) => Scaffold(
- appBar: _currentIndex != 4 ? _buildAppBar(context) : null,
- body: Container(
- decoration: const BoxDecoration(
- gradient: LinearGradient(
- colors: [MyApp.primaryColor, MyApp.secondaryColor],
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- ),
- ),
- child: _currentIndex == 4
- ? FavoritesPage(favoriteCards: favoriteCards, onRefresh: _loadFavorites)
- : _buildHome(),
- ),
- bottomNavigationBar: _buildBottomNavBar(),
- );
-
- AppBar _buildAppBar(BuildContext context) => AppBar(
- flexibleSpace: Padding(
- padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top - 20),
- child: Image.asset('assets/header_image.png', fit: BoxFit.cover),
- ),
- toolbarHeight: MediaQuery.of(context).size.height * 0.19,
- );
-
- Widget _buildBottomNavBar() => BottomNavigationBar(
- currentIndex: _currentIndex,
- onTap: (index) {
- setState(() {
- _currentIndex = index;
- filteredCards = _filterCardsByCategory(index);
- if (index == 4) _loadFavorites();
- });
- },
- items: const [
- BottomNavigationBarItem(
- icon: Icon(Icons.home_outlined),
- activeIcon: Icon(Icons.home),
- label: 'Inicio',
- ),
- BottomNavigationBarItem(
- icon: Icon(Icons.event_outlined),
- activeIcon: Icon(Icons.event),
- label: 'Actividades',
- ),
- BottomNavigationBarItem(
- icon: Icon(Icons.article_outlined),
- activeIcon: Icon(Icons.article),
- label: 'Noticias',
- ),
- BottomNavigationBarItem(
- icon: Icon(Icons.calendar_today_outlined),
- activeIcon: Icon(Icons.calendar_today),
- label: 'Fechas',
- ),
- BottomNavigationBarItem(
- icon: Icon(Icons.favorite_outline),
- activeIcon: Icon(Icons.favorite),
- label: 'Favoritos',
- ),
- ],
- );
-
- Widget _buildHome() => SafeArea(
- child: Column(children: [
- Expanded(child: _isLoading ? _buildLoader() : RefreshIndicator(
- onRefresh: _fetchData,
- child: filteredCards.isEmpty ? _buildEmptyState() : ListView.builder(
- itemCount: filteredCards.length,
- itemBuilder: (ctx, i) => CustomInfoCard(
- imagePath: "assets/icons/${filteredCards[i]['image']}",
- title: filteredCards[i]['title'],
- subtitle: filteredCards[i]['subtitle'],
- texto: filteredCards[i]['texto'],
- categoria: filteredCards[i]['categoria'],
- ),
- ),
- )),
- ]),
- );
-
- Widget _buildLoader() => const Center(child: CircularProgressIndicator(color: Colors.white));
-
- Widget _buildEmptyState() => Center(child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- const Icon(Icons.info_outline, size: 50, color: Colors.white),
- const SizedBox(height: 16),
- Text(
- 'No hay ${_getCategoryName(_currentIndex)} disponibles',
- style: const TextStyle(color: Colors.white, fontSize: 18),
- ),
- ],
- ));
-
- String _getCategoryName(int index) {
- switch (index) {
- case 0: return 'contenidos';
- case 1: return 'actividades';
- case 2: return 'noticias';
- case 3: return 'fechas importantes';
- default: return 'elementos';
- }
- }
- }
|