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'; // Ensure this path is correct 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) { return MaterialApp( title: 'Custom Info Cards', theme: ThemeData( primaryColor: primaryColor, appBarTheme: const AppBarTheme( backgroundColor: primaryColor, elevation: 0, ), ), home: const HomePage(), debugShowCheckedModeBanner: false, ); } } class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { List> infoCards = []; static const String googleSheetUrl = 'https://script.google.com/macros/s/AKfycbw1htmk7rlwLMOkOrpZ9ED-7ErWgMlYNtLKxoQ9QO-FopqTAhJuQkR7Gs1LxTJakbMT/exec'; @override void initState() { super.initState(); _loadInfoCards(); } Future _loadInfoCards() async { final prefs = await SharedPreferences.getInstance(); // Load default data if no saved data String? savedData = prefs.getString('infoCardsData'); setState(() { if (savedData != null) { try { final decoded = json.decode(savedData); infoCards = List>.from(decoded); } catch (e) { if (kDebugMode) { print('Error decoding saved data: $e'); } infoCards = _defaultInfoCards(); } } else { infoCards = _defaultInfoCards(); } }); await _fetchUpdatedData(); } List> _defaultInfoCards() { return [ { 'image': 'assets/icons/icono1.jpg', 'title': 'Tutorias de programación', 'subtitle': '8:00am a 11:30am En biblioteca Lázaro', }, ]; } Future _fetchUpdatedData() async { try { final response = await http.get(Uri.parse(googleSheetUrl)); if (response.statusCode == 200 && response.body.isNotEmpty) { try { final dynamic decodedData = json.decode(response.body); List> newData = []; if (decodedData is Map && decodedData.containsKey('feed')) { final entries = decodedData['feed']['entry'] as List?; if (entries != null) { for (var entry in entries) { final Map card = { 'image': entry['gsx\$image']?['\$t'] ?? '', 'title': entry['gsx\$title']?['\$t'] ?? '', 'subtitle': entry['gsx\$subtitle']?['\$t'] ?? '', }; newData.add(card); } } } else if (decodedData is List) { newData = List>.from(decodedData); } else { throw Exception('Unexpected JSON format'); } if (json.encode(newData) != json.encode(infoCards)) { setState(() { infoCards = newData; }); final prefs = await SharedPreferences.getInstance(); prefs.setString('infoCardsData', json.encode(newData)); if (kDebugMode) { print('Updated with new data from Google Sheet'); } } } catch (e) { if (kDebugMode) { print('Error parsing response data: $e'); } } } else { if (kDebugMode) { print('Failed to fetch updated data: ${response.statusCode}'); } } } catch (e) { if (kDebugMode) { print('Error fetching data: $e'); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( flexibleSpace: Padding( padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), child: Image.asset( 'assets/header_image.png', fit: BoxFit.cover, ), ), toolbarHeight: MediaQuery.of(context).size.height * 0.19, ), body: Container( decoration: const BoxDecoration( gradient: LinearGradient( colors: [MyApp.primaryColor, MyApp.secondaryColor], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: SafeArea( child: Column( children: [ Expanded( child: RefreshIndicator( onRefresh: _fetchUpdatedData, color: MyApp.primaryColor, child: ListView.builder( padding: const EdgeInsets.all(0), itemCount: infoCards.isEmpty ? 1 : infoCards.length, itemBuilder: (context, index) { // Show error widget if no data is available if (infoCards.isEmpty) { return Card( margin: const EdgeInsets.all(16), color: Colors.white, child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ const Icon(Icons.warning_amber_rounded, size: 48, color: Colors.orange), const SizedBox(height: 16), const Text( 'No se pudo cargar la información', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), const Text( 'Desliza hacia abajo para intentar de nuevo', textAlign: TextAlign.center, ), const SizedBox(height: 16), ElevatedButton( onPressed: () => _fetchUpdatedData(), style: ElevatedButton.styleFrom( backgroundColor: MyApp.primaryColor, ), child: const Text('Reintentar'), ), ], ), ), ); } // Show data card final card = infoCards[index]; return CustomInfoCard( imagePath: "assets/icons/${card['image']}", title: card['title'], subtitle: card['subtitle'], ); }, ), ), ), ], ), ), ), ); } }