diff --git a/lib/src/controllers/categoty_controller.dart b/lib/src/controllers/categoty_controller.dart new file mode 100644 index 0000000..eb3019b --- /dev/null +++ b/lib/src/controllers/categoty_controller.dart @@ -0,0 +1,46 @@ +import 'dart:convert'; // Importa el paquete para trabajar con JSON. +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:primer_practica/src/http_api/category_service.dart'; +import 'package:primer_practica/src/models/categoryModel.dart'; + + +class CategoryController { + final Connectivity _connectivity = Connectivity(); // Crea una instancia de Connectivity para verificar el estado de la red. + + Future> getCategories() async { + // Inicializa la respuesta por defecto. + Map mapResp = { + 'ok': false, + 'message': 'No hay categorias', + 'data': null + }; + + // Verifica la conectividad. + ConnectivityResult connectivityResult = (await _connectivity.checkConnectivity()) as ConnectivityResult; + + // Si hay conexión a internet. + if (connectivityResult != ConnectivityResult.none) { + if (connectivityResult == ConnectivityResult.wifi || connectivityResult == ConnectivityResult.mobile) { + CategoryService categoryApi = CategoryService(); // Crea una instancia de CategoryApi. + Map respGet = await categoryApi.getCategories(); // Obtiene las categorías desde la API. + + // Si la respuesta de la API es exitosa. + if (respGet['statusCode'] == 200) { + try { + var decodeResp = json.decode(respGet['body']); // Decodifica la respuesta JSON. + List listCategories = CategoryModel.fromJsonArray(decodeResp['data']); // Convierte los datos en una lista de categorías. + mapResp['ok'] = true; // Marca la operación como exitosa. + mapResp['message'] = "${listCategories.length} categorias encontradas"; // Actualiza el mensaje con el número de categorías encontradas. + mapResp['data'] = listCategories; // Asigna los datos de las categorías a la respuesta. + } catch (e) { + mapResp['message'] = "Error en operador $e"; // Captura y asigna cualquier error durante la decodificación. + } + } else { + mapResp['message'] = "${respGet['body']}"; // Asigna el mensaje de error de la API a la respuesta. + } + } + } + + return mapResp; // Retorna la respuesta + } +} diff --git a/lib/src/http_api/category_service.dart b/lib/src/http_api/category_service.dart index fd7db71..e3bc062 100644 --- a/lib/src/http_api/category_service.dart +++ b/lib/src/http_api/category_service.dart @@ -1,13 +1,13 @@ -import 'package:flutter/foundation.dart'; // Para usar kDebugMode +import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:primer_practica/environments/urls.dart' as api; class CategoryService { - static const String apiBaseUrl = 'categoria'; // Cambia esto a la URL base de tu API + static const String apiBaseUrl = 'categoria'; // Método para obtener las categorías Future> getCategories({int offset = 0, int max = 100}) async { - String url = '${{api.apiApp}}/$apiBaseUrl?offset=$offset&max=$max'; + String url = '${api.apiApp}/$apiBaseUrl?offset=$offset&max=$max'; if (kDebugMode) { print('Url -> $url'); diff --git a/lib/src/http_api/connectivity_result.dart b/lib/src/http_api/connectivity_result.dart new file mode 100644 index 0000000..c3e2866 --- /dev/null +++ b/lib/src/http_api/connectivity_result.dart @@ -0,0 +1,28 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; + +Future checkConnecticity() async { + final ConnectivityResult connectivityResult = await (Connectivity().checkConnectivity()); + +// This condition is for demo purposes only to explain every connection type. +// Use conditions which work for your requirements. + if (connectivityResult==ConnectivityResult.mobile) { +// Mobile network available. + } else if (connectivityResult==ConnectivityResult.wifi) { +// Wi-fi is available. +// Note for Android: +// When both mobile and Wi-Fi are turned on system will return Wi-Fi only as active network type + } else if (connectivityResult==ConnectivityResult.ethernet) { +// Ethernet connection available. + } else if (connectivityResult==ConnectivityResult.vpn) { +// Vpn connection active. +// Note for iOS and macOS: +// There is no separate network interface type for [vpn]. +// It returns [other] on any device (also simulator) + } else if (connectivityResult==ConnectivityResult.bluetooth) { +// Bluetooth connection available. + } else if (connectivityResult==ConnectivityResult.other) { +// Connected to a network which is not in the above mentioned networks. + } else if (connectivityResult==ConnectivityResult.none) { +// No available network types + } +} diff --git a/lib/src/models/categoryModel.dart b/lib/src/models/categoryModel.dart index dc611b4..8665a89 100644 --- a/lib/src/models/categoryModel.dart +++ b/lib/src/models/categoryModel.dart @@ -3,51 +3,72 @@ class CategoryModel { int id; int? version; - String clave; - String nombre; - int? fechaCreado; - String categoria; - bool? activo; + String? key; + String? name; + int? createdDate; + CategoryModel? parentCategory; + List? subCategories; + bool? active; CategoryModel({ required this.id, this.version, - required this.clave, - required this.nombre, - this.fechaCreado, - required this.categoria, - this.activo, + this.key, + this.name, + this.createdDate, + this.parentCategory, + this.subCategories, + this.active, }); // Método para crear una instancia de CategoryModel desde un JSON - factory CategoryModel.fromJson(Map json) => CategoryModel( - id: json['id'], - version: json['version'] ?? 0, - clave: json['clave'] ?? '', - nombre: json['nombre'] ?? '', - fechaCreado: json['fechaCreado'], - categoria: json['categoria'] ?? '', - activo: json['activo'], - ); + factory CategoryModel.fromJson(Map json) { + return CategoryModel( + id: json['id'], + version: json['version'], + key: json['clave'], + name: json['nombre'], + createdDate: json['fechaCreado'], + parentCategory: json['categoria'] != null + ? CategoryModel.fromJson(json['categoria']) + : null, + subCategories: json['categorias'] != null + ? CategoryModel.fromJsonArray(json['categorias']) + : [], + active: json['activo'], + ); + } - // Método para convertir una instancia de CategoryModel a JSON - Map toJson() => { - 'id': id, - 'version': version, - 'clave': clave, - 'nombre': nombre, - 'fechaCreado': fechaCreado, - 'categoria': categoria, - 'activo': activo, - }; + // Este metodo sirve para para convertir una instancia de CategoryModel a JSON + Map toJson() { + return { + "id": id, + "version": version, + "clave": key, + "nombre": name, + "fechaCreado": createdDate, + "categoria": parentCategory?.toJson(), + "categorias": subCategories != null + ? CategoryModel.toJsonArray(subCategories!) + : [], + "activo": active, + }; + } - // Método para crear una lista de CategoryModel desde una lista de JSONs - static List fromJsonArray(List json) { - return json.map((data) => CategoryModel.fromJson(data)).toList(); + // Método para crear una lista de CategoryModel desde un JSON + static List fromJsonArray(json) { + if (json == null) return []; + var list = json as List; + List listResult = + list.map((data) => CategoryModel.fromJson(data)).toList(); + return listResult; } - // Método para convertir una lista de CategoryModel a una lista de JSONs - static List> toJsonArray(List list) { - return list.map((item) => item.toJson()).toList(); + static toJsonArray(List list) { + List> listMap = []; + for (CategoryModel item in list) { + listMap.add(item.toJson()); + } + return listMap; } } diff --git a/lib/src/pages/category/lista_categorias.dart b/lib/src/pages/category/lista_categorias.dart new file mode 100644 index 0000000..15f0e3d --- /dev/null +++ b/lib/src/pages/category/lista_categorias.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:primer_practica/src/controllers/categoty_controller.dart'; +import 'package:primer_practica/src/shared_widgets/widget_mensajes.dart' as msg_shared; +import 'package:primer_practica/src/models/categoryModel.dart'; + +class ListaCategorias extends StatelessWidget { + final CategoryController categoryCtrl = CategoryController(); // Crea una instancia del controlador de categorías + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: FutureBuilder( + future: categoryCtrl.getCategories(), // Llama al método getCategories del controlador + builder: (context, AsyncSnapshot> snapshot) { + if (snapshot.hasData) { + if (snapshot.data!['ok']) { + if (snapshot.data!['data'].length > 0) { + return ListView.builder( + itemBuilder: (BuildContext context, int index) { + CategoryModel category = snapshot.data!['data'][index]; + return Card( + child: ListTile( + title: Text(category.name?? 'No Name'), // Muestra el nombre de la categoría + leading: const Icon(Icons.touch_app), + onTap: () { + // Navegar al listado de servicios + }, + ), + ); + }, + itemCount: snapshot.data!['data'].length, + ); + } else { + return msg_shared.alertWait("No se encontraron categorías"); + } + } else { + return msg_shared.alertDanger("Error: ${snapshot.data!['message']}"); + } + } else if (snapshot.hasError) { + return msg_shared.alertDanger("Error: ${snapshot.error.toString()}"); + } else { + return const Center( + child: CircularProgressIndicator(), // Muestra un indicador de progreso mientras se cargan los datos + ); + } + }, + ), + ), + ); + } +} diff --git a/lib/src/pages/home_page.dart b/lib/src/pages/home_page.dart index 7e6f716..6cce8ae 100644 --- a/lib/src/pages/home_page.dart +++ b/lib/src/pages/home_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:primer_practica/src/pages/category/lista_categorias.dart'; // Importa el widget de la lista de categorías class HomePage extends StatelessWidget { const HomePage({Key? key}) : super(key: key); @@ -12,8 +13,8 @@ class HomePage extends StatelessWidget { actions: [ IconButton( icon: const Icon(Icons.search), - onPressed: (){ - + onPressed: () { + // Acción de búsqueda }, ) ], @@ -40,21 +41,7 @@ class HomePage extends StatelessWidget { ], ), ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Hola home page'), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () { - Navigator.pushNamed(context, 'articles'); - }, - child: const Text('Ir a Articles Page'), - ), - ], - ), - ), + body: ListaCategorias(), // Usa el widget de la lista de categorías en el body floatingActionButton: FloatingActionButton( onPressed: () { Navigator.pushNamed(context, 'login'); diff --git a/lib/src/pages/login_page.dart b/lib/src/pages/login_page.dart index a1a43a7..58abfc6 100644 --- a/lib/src/pages/login_page.dart +++ b/lib/src/pages/login_page.dart @@ -10,13 +10,13 @@ class LoginPage extends StatelessWidget { title: const Text('Login Page'), // Título de la barra ), body: Padding( // Espacia el contenido del cuerpo - padding: const EdgeInsets.all(180.0), // Padding alrededor del contenido + padding: const EdgeInsets.all(160.90), // Padding alrededor del contenido child: Center( // Centra el contenido en la pantalla child: Column( // Organiza los widgets en una columna vertical mainAxisAlignment: MainAxisAlignment.center, // Centra verticalmente children: [ // Lista de widgets en la columna - Image.asset('assets/img/logo_visorus.jpg', height: 100), // Muestra el logo con altura de 100 - const SizedBox(height: 80), // Espacio de 80 píxeles entre el logo y el primer campo de texto + Image.asset('assets/img/logo_visorus.jpg', height: 90), // Muestra el logo con altura de 100 + const SizedBox(height: 40), // Espacio de 80 píxeles entre el logo y el primer campo de texto TextField( // Campo de texto para el usuario decoration: const InputDecoration( labelText: 'Usuario', // Etiqueta del campo de texto @@ -31,7 +31,7 @@ class LoginPage extends StatelessWidget { border: OutlineInputBorder(), // Borde del campo de texto ), ), - const SizedBox(height: 20), // Espacio de 20 píxeles antes del botón + const SizedBox(height: 30), // Espacio de 20 píxeles antes del botón ElevatedButton( onPressed: () { // Acción al presionar el botón Navigator.pushNamed(context, 'home'); // Navega a la página 'home' diff --git a/lib/src/shared_widgets/widget_mensajes.dart b/lib/src/shared_widgets/widget_mensajes.dart new file mode 100644 index 0000000..36a5c1c --- /dev/null +++ b/lib/src/shared_widgets/widget_mensajes.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; + +// Widget para mostrar alertas de error +Widget alertDanger(String message) { + return Container( + // El contenedor se expande a todo el ancho disponible + width: double.infinity, + // padding alrededor del contenido + padding: const EdgeInsets.all(8.0), + //color de fondo del contenedor + color: const Color.fromRGBO(192, 57, 43, 0.5), + child: Row( + // fila para los íconos y el texto + children: [ + const Icon( + Icons.error, // Icono de error + color: Colors.white, // Color del icono + ), + const SizedBox( + width: 10.0, // Espacio horizontal entre el icono y el texto + ), + Expanded( + // Expande el widget de texto para llenar el espacio restante + child: Text( + message, // Mensaje de error + style: const TextStyle(color: Colors.white), + ), + ) + ], + )); +} + +// Widget para mostrar alertas de éxito +Widget alertSuccess(String message) { + return Container( + // El contenedor se expande a todo el ancho disponible + width: double.infinity, + padding: const EdgeInsets.all(8.0), + color: Colors.lightGreen, + child: Row( + children: [ + const Icon( + Icons.check_circle_outline, + color: Colors.white, + ), + const SizedBox( + width: 10.0, + ), + Expanded( + child: Text( + message, + style: const TextStyle(color: Colors.white), + ), + ) + ], + )); +} + +// Widget para mostrar alertas de información +Widget alertWait(String message) { + return Container( + // El contenedor se expande a todo el ancho disponible + width: double.infinity, + padding: const EdgeInsets.all(8.0), + color: Colors.lightBlueAccent, + child: Row( + children: [ + const Icon( + Icons.info, + color: Colors.white, + ), + const SizedBox( + width: 10.0, + ), + Expanded( + child: Text( + message, + style: const TextStyle(color: Colors.white), + ), + ) + ], + )); +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index ad535f5..be95aee 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,8 @@ import FlutterMacOS import Foundation -import connectivity_plus +import connectivity_plus_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) + ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 58e5797..38c54c6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -53,18 +53,50 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: "3e7d1d9dbae40ae82cbe6c23c518f0c4ffe32764ee9749b9a99d32cbac8734f6" + sha256: "3f8fe4e504c2d33696dac671a54909743bc6a902a9bb0902306f7a2aed7e528e" url: "https://pub.dev" source: hosted - version: "6.0.4" + version: "2.3.9" + connectivity_plus_linux: + dependency: transitive + description: + name: connectivity_plus_linux + sha256: "3caf859d001f10407b8e48134c761483e4495ae38094ffcca97193f6c271f5e2" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + connectivity_plus_macos: + dependency: transitive + description: + name: connectivity_plus_macos + sha256: "488d2de1e47e1224ad486e501b20b088686ba1f4ee9c4420ecbc3b9824f0b920" + url: "https://pub.dev" + source: hosted + version: "1.2.6" connectivity_plus_platform_interface: dependency: transitive description: name: connectivity_plus_platform_interface - sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204" + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + url: "https://pub.dev" + source: hosted + version: "1.2.4" + connectivity_plus_web: + dependency: transitive + description: + name: connectivity_plus_web + sha256: "81332be1b4baf8898fed17bb4fdef27abb7c6fd990bf98c54fd978478adf2f1a" + url: "https://pub.dev" + source: hosted + version: "1.2.5" + connectivity_plus_windows: + dependency: transitive + description: + name: connectivity_plus_windows + sha256: "535b0404b4d5605c4dd8453d67e5d6d2ea0dd36e3b477f50f31af51b0aeab9dd" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "1.2.2" cupertino_icons: dependency: "direct main" description: @@ -136,6 +168,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" leak_tracker: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8ac05b9..2b2cd2b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,7 +31,7 @@ dependencies: flutter: sdk: flutter http: ^1.2.2 - connectivity_plus: ^6.0.4 + connectivity_plus: ^2.3.6 # The following adds the Cupertino Icons font to your application. diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8777c93..8083d74 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,7 +6,7 @@ #include "generated_plugin_registrant.h" -#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { ConnectivityPlusWindowsPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index cc1361d..8cf5d42 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,7 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST - connectivity_plus + connectivity_plus_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST