c90Beretta месяцев назад: 5
Родитель
Сommit
eac6afd397

+ 3 - 0
.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+    "java.configuration.updateBuildConfiguration": "interactive"
+}

+ 4 - 2
android/app/build.gradle

@@ -45,7 +45,7 @@ android {
         applicationId "com.example.sis_flutter"
         // You can update the following values to match your application needs.
         // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
-        minSdkVersion flutter.minSdkVersion
+        minSdkVersion 
         targetSdkVersion flutter.targetSdkVersion
         versionCode flutterVersionCode.toInteger()
         versionName flutterVersionName
@@ -64,4 +64,6 @@ flutter {
     source '../..'
 }
 
-dependencies {}
+dependencies {
+       implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+}

+ 1 - 0
android/gradle.properties

@@ -1,3 +1,4 @@
 org.gradle.jvmargs=-Xmx4G
 android.useAndroidX=true
 android.enableJetifier=true
+org.gradle.java.home=/path/to/your/java11

+ 1 - 0
android/settings.gradle

@@ -22,6 +22,7 @@ pluginManagement {
 }
 
 plugins {
+
     id "dev.flutter.flutter-plugin-loader" version "1.0.0"
     id "com.android.application" version "7.3.0" apply false
 }

+ 2 - 0
lib/main.dart

@@ -2,6 +2,7 @@
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import 'package:sis_flutter/viewmodels/tarea_view_model.dart';
+import 'package:sis_flutter/views/profile/profile_screen.dart';
 import '../themes/themes.dart';
 import 'views/home/home_screen.dart';
 import 'views/login/login_screen.dart';
@@ -40,6 +41,7 @@ class MyApp extends StatelessWidget {
         MainScreen.route: (context) => const MainScreen(),
         LoginScreen.route: (context) => const LoginScreen(),
         HomeScreen.route: (context) => const HomeScreen(),
+        ProfileScreen.route: (context) => const ProfileScreen(),
       },
     );
   }

+ 2 - 0
lib/plugins/audio_plugin.dart

@@ -1,3 +1,5 @@
+// ignore_for_file: avoid_print
+
 import 'dart:io';
 import 'dart:math';
 

+ 82 - 82
lib/plugins/camara_plugin.dart

@@ -1,97 +1,97 @@
-import 'package:camera/camera.dart';
-//import 'package:image_picker_web/image_picker_web.dart';
-import '../interfaces/camara_interface.dart';
-import '../models/media_model.dart';
-import "package:universal_html/html.dart" as html;
+// import 'package:camera/camera.dart';
+// //import 'package:image_picker_web/image_picker_web.dart';
+// import '../interfaces/camara_interface.dart';
+// import '../models/media_model.dart';
+// import "package:universal_html/html.dart" as html;
 
-class CamaraPlugin implements CamaraInterface {
-  XFile? _currentImage;
-  List<XFile>? _currentImageList;
+// class CamaraPlugin implements CamaraInterface {
+//   XFile? _currentImage;
+//   List<XFile>? _currentImageList;
 
-  @override
-  Future<Media?> imageFromCamera() async {
-    /* _currentImage = await ImagePickerWeb().pickImage(
-        source: ImageSource.camera, maxWidth: 1024.0, maxHeight: 768.0
-      );
-    if (_currentImage == null) return Future.value(null);
-    String directorio = (await getApplicationDocumentsDirectory()).path;
-    String fecha = DateTime.now().millisecondsSinceEpoch.toString();
-    await _currentImage!.saveTo("$directorio/$fecha.jpg");
+//   @override
+//   Future<Media?> imageFromCamera() async {
+//     /* _currentImage = await ImagePickerWeb().pickImage(
+//         source: ImageSource.camera, maxWidth: 1024.0, maxHeight: 768.0
+//       );
+//     if (_currentImage == null) return Future.value(null);
+//     String directorio = (await getApplicationDocumentsDirectory()).path;
+//     String fecha = DateTime.now().millisecondsSinceEpoch.toString();
+//     await _currentImage!.saveTo("$directorio/$fecha.jpg");
 
-    return Media(
-      ruta: _currentImage?.path,
-      tipo: 'imagen',
-    ); */
-  }
+//     return Media(
+//       ruta: _currentImage?.path,
+//       tipo: 'imagen',
+//     ); */
+//   }
 
-  @override
-  Future<Media?> imageFromGallery() async {
-    /* _currentImage = await ImagePicker().pickImage(source: ImageSource.gallery);
-    if (_currentImage == null) return Future.value(null);
+//   @override
+//   Future<Media?> imageFromGallery() async {
+//     /* _currentImage = await ImagePicker().pickImage(source: ImageSource.gallery);
+//     if (_currentImage == null) return Future.value(null);
 
-    return Media(
-      ruta: _currentImage?.path,
-      tipo: 'imagen',
-    ); */
-  }
+//     return Media(
+//       ruta: _currentImage?.path,
+//       tipo: 'imagen',
+//     ); */
+//   }
 
-  @override
-  Future<List<Media>?> multiImageFromGallery() async {
-    /* _currentImageList = await ImagePicker().pickMultiImage();
-    if (_currentImageList == null) return Future.value(null);
-    final List<Media> mediaList = [];
+//   @override
+//   Future<List<Media>?> multiImageFromGallery() async {
+//     /* _currentImageList = await ImagePicker().pickMultiImage();
+//     if (_currentImageList == null) return Future.value(null);
+//     final List<Media> mediaList = [];
 
-    for (var media in _currentImageList!) {
-      mediaList.add(Media(
-        ruta: media.path,
-        tipo: 'imagen',
-      ));
-    }
-    return mediaList; */
-  }
+//     for (var media in _currentImageList!) {
+//       mediaList.add(Media(
+//         ruta: media.path,
+//         tipo: 'imagen',
+//       ));
+//     }
+//     return mediaList; */
+//   }
 
-  @override
-  Future<Media?> videoFromCamera() async {
-    //html.File? videoFile = await ImagePickerWeb.getVideoAsFile();
-    /* ImagePicker picker = ImagePicker();
-    _currentImage = await picker.pickVideo(source: ImageSource.camera, maxDuration:const Duration(seconds: 15));
-    if (_currentImage == null) return Future.value(null);
-    //FlutterFFmpeg _flutterFFmpeg = FlutterFFmpeg();
-    String directorio = (await getApplicationDocumentsDirectory()).path;
-    String fecha = DateTime.now().millisecondsSinceEpoch.toString();
-    String laruta = "$directorio/$fecha.mp4";
-    await _currentImage!.saveTo(laruta); */
+//   @override
+//   Future<Media?> videoFromCamera() async {
+//     //html.File? videoFile = await ImagePickerWeb.getVideoAsFile();
+//     /* ImagePicker picker = ImagePicker();
+//     _currentImage = await picker.pickVideo(source: ImageSource.camera, maxDuration:const Duration(seconds: 15));
+//     if (_currentImage == null) return Future.value(null);
+//     //FlutterFFmpeg _flutterFFmpeg = FlutterFFmpeg();
+//     String directorio = (await getApplicationDocumentsDirectory()).path;
+//     String fecha = DateTime.now().millisecondsSinceEpoch.toString();
+//     String laruta = "$directorio/$fecha.mp4";
+//     await _currentImage!.saveTo(laruta); */
 
 
-   /*  String inputPath = laruta;//'path/to/your/original/video.mp4';
-    String outputPath =laruta;// 'path/to/your/compressed/video.mp4';
+//    /*  String inputPath = laruta;//'path/to/your/original/video.mp4';
+//     String outputPath =laruta;// 'path/to/your/compressed/video.mp4';
 
-    String command = '-i $inputPath -c:v libx264 -preset ultrafast $outputPath';
-    int rc = await _flutterFFmpeg.execute(command); */
+//     String command = '-i $inputPath -c:v libx264 -preset ultrafast $outputPath';
+//     int rc = await _flutterFFmpeg.execute(command); */
 
-    /* var thumbnailFile = await VideoCompress.getFileThumbnail(
-      laruta.toString(),
-      quality: 50, // default(100)
-      position: -1 // default(-1)
-    ); */
+//     /* var thumbnailFile = await VideoCompress.getFileThumbnail(
+//       laruta.toString(),
+//       quality: 50, // default(100)
+//       position: -1 // default(-1)
+//     ); */
 
-    /* MediaInfo? info = await VideoCompress.compressVideo(
-      laruta.toString(),
-      //quality: VideoQuality.DefaultQuality,
-      quality: VideoQuality.LowQuality,
-      //quality: VideoQuality.Res640x480Quality,
-      deleteOrigin: false,
-    );
+//     /* MediaInfo? info = await VideoCompress.compressVideo(
+//       laruta.toString(),
+//       //quality: VideoQuality.DefaultQuality,
+//       quality: VideoQuality.LowQuality,
+//       //quality: VideoQuality.Res640x480Quality,
+//       deleteOrigin: false,
+//     );
     
-    await info!.file!.copy(laruta);
-    print("que onda con el path");
-    print(info.file!.path);
-    print(laruta);
-    //info!.file;
+//     await info!.file!.copy(laruta);
+//     print("que onda con el path");
+//     print(info.file!.path);
+//     print(laruta);
+//     //info!.file;
 
-    return Media(
-      ruta: laruta,
-      tipo: 'video',
-    ); */
-  }
-}
+//     return Media(
+//       ruta: laruta,
+//       tipo: 'video',
+//     ); */
+//   }
+// }

+ 1 - 0
lib/router/app_router.dart

@@ -0,0 +1 @@
+///! TODO: Add the routes for the app

+ 17 - 0
lib/services/usuario_session_service.dart

@@ -0,0 +1,17 @@
+import 'package:sis_flutter/data/session/session_storage.dart';
+import 'package:sis_flutter/models/usuario_model.dart';
+
+Future<Usuario> getUsuarioFromSessionStorage() async {
+  final session = SessionStorage();
+  final id = await session.getId();
+  final email = await session.getCorreo();
+  final name = await session.getNombre();
+  final empresa = await session.getEmpresa();
+
+  return Usuario(
+    id: id!,
+    email: email,
+    name: name,
+    empresa: empresa,
+  );
+}

+ 31 - 0
lib/services/usuarios_service.dart

@@ -0,0 +1,31 @@
+import 'dart:convert';
+import 'package:sis_flutter/models/usuario_model.dart';
+import 'package:sis_flutter/services/base_service.dart';
+
+class UsuariosService extends BaseService {
+  final endpoint = "/v1/usuario";
+
+  Future<List<Usuario>> getUsuarios(
+      int limit, int page, String idUsuario) async {
+    var r = await get(endpoint, headers: {
+      'Content-Type': 'application/json'
+    }, queryParameters: {
+      'usuario': idUsuario,
+      'limit': limit.toString(),
+      'page': page.toString(),
+    });
+
+    var body = jsonDecode(r.body);
+    var usuariosBody =
+        (body['resultado']).cast<Map<String, dynamic>>().toList();
+
+    final List<Usuario> usuarioList = [];
+    if (r.statusCode == 200) {
+      for (var i in usuariosBody) {
+        Usuario usuarioFromJson = Usuario.fromJson(i);
+        usuarioList.add(usuarioFromJson);
+      }
+    }
+    return usuarioList;
+  }
+}

+ 8 - 9
lib/viewmodels/login_view_model.dart

@@ -18,11 +18,11 @@ class LoginViewModel extends ChangeNotifier {
   List<String> _permisos = [];
   List<String> get permisos => _permisos;
 
-  String _name = "";
-  String get name => _name;
+  String name = "";
+  String get _name => name;
 
-  String _email = "";
-  String get email => _email;
+  String email = "";
+  String get _email => email;
 
   String _empresa = "";
   String get empresa => _empresa;
@@ -52,7 +52,6 @@ class LoginViewModel extends ChangeNotifier {
           SessionStorage().saveId(apiResponse.detalle?['id']);
           SessionStorage().saveCorreo(apiResponse.detalle!['email']);
           SessionStorage().saveNombre(apiResponse.detalle!['name']);
-          _name = apiResponse.detalle!['name'];
           _status = Status.authenticated;
           _cargando = false;
           notifyListeners();
@@ -96,13 +95,13 @@ class LoginViewModel extends ChangeNotifier {
   }
 
   setValores() async {
-    _name = (await SessionStorage().getNombre()).toString();
-    _email = (await SessionStorage().getCorreo()).toString();
+    name = (await SessionStorage().getNombre()).toString();
+    email = (await SessionStorage().getCorreo()).toString();
     _empresa = (await SessionStorage().getEmpresa()).toString();
     notifyListeners();
   }
 
-  bool validarEmail(String email) =>
+  bool validarEmail(String correo) =>
       RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
-          .hasMatch(email.trim());
+          .hasMatch(correo.trim());
 }

+ 12 - 6
lib/viewmodels/usuarios_view_model.dart

@@ -1,7 +1,6 @@
 import 'dart:convert';
-
 import 'package:flutter/material.dart';
-
+import 'package:sis_flutter/services/usuarios_service.dart';
 import '../models/models.dart';
 import '../services/base_service.dart';
 
@@ -94,7 +93,7 @@ class UsuariosViewModel extends ChangeNotifier {
   }) async {
     //?TEMPORAL
     modelo.name = name;
-    modelo.name = email;
+    modelo.email = email;
     modelo.telefono = telefono;
     modelo.creado = DateTime.now();
     notifyListeners();
@@ -132,11 +131,18 @@ class UsuariosViewModel extends ChangeNotifier {
     }
   }
 
+  //? get Usuarios para el dropdownsearch
+  Future<List<Usuario>> fetchUsuarios(
+      String idUsuario, int limit, int page) async {
+    final usuariosList = await UsuariosService().getUsuarios(10, 1, idUsuario);
+    return usuariosList;
+  }
+
   bool validarUsuario({
-    required String name,
-    required String email,
+    required String nombre,
+    required String correo,
   }) {
-    if (name.isEmpty || email.isEmpty) {
+    if (nombre.isEmpty || correo.isEmpty) {
       return false;
     }
     return true;

+ 24 - 154
lib/views/home/home_screen.dart

@@ -2,16 +2,12 @@
 
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
-import 'package:sis_flutter/models/actividad_model.dart';
 import 'package:sis_flutter/themes/themes.dart';
-
-import 'package:sis_flutter/viewmodels/login_view_model.dart';
-import 'package:sis_flutter/viewmodels/tarea_view_model.dart';
+import 'package:sis_flutter/views/home/home_view.dart';
+import 'package:sis_flutter/views/profile/profile_screen.dart';
 import 'package:sis_flutter/widgets/app_drawer.dart';
 import 'package:sis_flutter/widgets/custom_bottom_navigation_bar.dart';
 
-import '../../data/session/session_storage.dart';
-
 class HomeScreen extends StatefulWidget {
   static const String route = '/home';
 
@@ -21,10 +17,19 @@ class HomeScreen extends StatefulWidget {
   Formulario createState() => Formulario();
 }
 
-class Formulario extends State<HomeScreen> {
+class Formulario extends State<HomeScreen> with SingleTickerProviderStateMixin {
+  late TabController _tabController;
   @override
   void initState() {
     super.initState();
+    _tabController = TabController(length: 2, vsync: this);
+  }
+
+  @override
+  void dispose() {
+    // TODO: implement dispose
+    super.dispose();
+    _tabController.dispose();
   }
 
   @override
@@ -37,155 +42,20 @@ class Formulario extends State<HomeScreen> {
       drawerScrimColor:
           const Color.fromARGB(255, 119, 119, 119).withOpacity(0.5),
       drawer: AppDrawer(),
-      body: const SafeArea(child: HomeBody()),
-      bottomNavigationBar: const CustomBottomNavigationBar(),
-    );
-  }
-}
-
-class HomeBody extends StatefulWidget {
-  const HomeBody({
-    super.key,
-  });
-
-  @override
-  State<HomeBody> createState() => _HomeBodyState();
-}
-
-class _HomeBodyState extends State<HomeBody> {
-  @override
-  void initState() {
-    super.initState();
-    Future(
-      () async {
-        final idusuario =
-            Provider.of<LoginViewModel>(context, listen: false).idUsuario;
-        Provider.of<PrioridadesViewMode>(context, listen: false)
-            .fetchActividad(idusuario);
-      },
-    );
-  }
-
-  @override
-  void dispose() {
-    super.dispose();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    var vm = Provider.of<PrioridadesViewMode>(context);
-    // var prioridades = vm.prioridades;
-    var actividadDetalle = vm.actividadList;
-    var usuario = Provider.of<LoginViewModel>(context, listen: false);
-    final height = MediaQuery.of(context).size.height;
-
-    return SingleChildScrollView(
-      child: Column(
-        children: [
-          Container(
-            padding: const EdgeInsets.all(10),
-            decoration: const BoxDecoration(
-                color: Colors.white,
-                borderRadius:
-                    BorderRadius.vertical(bottom: Radius.circular(15))),
-            child: Padding(
-              padding: const EdgeInsets.all(8.0),
-              child: Column(
-                  mainAxisAlignment: MainAxisAlignment.start,
-                  crossAxisAlignment: CrossAxisAlignment.start,
-                  children: [
-                    const Text("Bienvenido ",
-                        style: TextStyle(
-                          fontSize: 25,
-                        )),
-                    Text(usuario.name,
-                        style: const TextStyle(
-                            fontSize: 45,
-                            fontWeight: FontWeight.bold,
-                            height: -1)),
-                    const SizedBox(height: 10),
-                    Container(
-                      decoration: BoxDecoration(
-                        borderRadius: BorderRadius.circular(10),
-                        color: const Color.fromARGB(141, 232, 234, 237),
-                      ),
-                      child: const TextField(
-                        decoration: InputDecoration(
-                            contentPadding: EdgeInsets.symmetric(vertical: 15),
-                            border: InputBorder.none,
-                            prefix: Icon(
-                              Icons.search,
-                              color: Colors.black87,
-                            ),
-                            hintText: "Busqueda por Usuarios",
-                            hintStyle:
-                                TextStyle(color: Colors.grey, fontSize: 15)),
-                      ),
-                    ),
-                  ]),
-            ),
-          ),
-          const SizedBox(height: 10),
-          SizedBox(
-            width: double.infinity,
-            height: height * 0.9,
-            child: ListView.builder(
-                itemCount: actividadDetalle.length,
-                itemBuilder: (context, index) {
-                  var actividad = actividadDetalle[index];
-
-                  return _infoCard(actividad);
-                }),
-          ),
+      body: TabBarView(
+        controller: _tabController,
+        children: <Widget>[
+          const HomeBody(),
+          const ProfileScreen(),
         ],
       ),
-    );
-  }
-
-  Card _infoCard(Actividad actividad) {
-    List<Widget> tareaWidgets = [];
-    for (var tarea in actividad.tareas!) {
-      final posicion = (actividad.tareas!.indexOf(tarea) + 1).toString();
-      tareaWidgets.add(
-        ListTile(
-          title: Text(tarea.contenido!),
-          subtitle: Text("Subtarea $posicion"),
-        ),
-      );
-    }
-    return Card(
-      margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8),
-      elevation: 4,
-      shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(6.0)),
-      child: ExpansionTile(
-        backgroundColor: Colors.white24,
-        leading: const Icon(Icons.local_fire_department_rounded),
-        title: Text(
-          "${actividad.nombreProyecto!} - ${actividad.nombre!} ",
-          style: const TextStyle(
-              fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black54),
-          strutStyle: const StrutStyle(fontWeight: FontWeight.bold),
-        ),
-        trailing: const Icon(Icons.arrow_drop_down),
-        subtitle: Row(
-          mainAxisAlignment: MainAxisAlignment.start,
-          crossAxisAlignment: CrossAxisAlignment.center,
-          children: [
-            Text(actividad.avance!),
-            const SizedBox(width: 10),
-            Expanded(
-              child: ClipRRect(
-                borderRadius: const BorderRadius.all(Radius.circular(10)),
-                child: LinearProgressIndicator(
-                    value: double.parse(actividad.avance!) / 100,
-                    backgroundColor: Colors.grey,
-                    valueColor: const AlwaysStoppedAnimation(AppTheme.primary)),
-              ),
-            ),
-          ],
-        ),
-        children: tareaWidgets,
-      ),
+      bottomNavigationBar: CustomBottomNavigationBar(
+          indexValue: _tabController.index,
+          onIndexSelected: (index) {
+            setState(() {
+              _tabController.index = index;
+            });
+          }),
     );
   }
 }

+ 181 - 0
lib/views/home/home_view.dart

@@ -0,0 +1,181 @@
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import 'package:sis_flutter/data/session/session_storage.dart';
+import 'package:sis_flutter/models/usuario_model.dart';
+import 'package:sis_flutter/services/usuario_session_service.dart';
+import 'package:sis_flutter/viewmodels/login_view_model.dart';
+import 'package:sis_flutter/viewmodels/tarea_view_model.dart';
+import 'package:sis_flutter/viewmodels/viewmodels.dart';
+import 'package:sis_flutter/widgets/app_dropdown_search.dart';
+
+import '../../models/actividad_model.dart';
+import '../../themes/themes.dart';
+
+class HomeBody extends StatefulWidget {
+  const HomeBody({
+    super.key,
+  });
+
+  @override
+  State<HomeBody> createState() => _HomeBodyState();
+}
+
+class _HomeBodyState extends State<HomeBody> {
+  Usuario? _usuario;
+  List<Usuario> _usuariosList = [];
+  TextEditingController _usuariosController = TextEditingController();
+
+  @override
+  void initState() {
+    super.initState();
+    final idUsuario =
+        Provider.of<LoginViewModel>(context, listen: false).idUsuario;
+    _fetchActividad(idUsuario);
+    _loadUsuarios();
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+  }
+
+  Future<void> _loadUsuarios() async {
+    final usuario = await getUsuarioFromSessionStorage();
+    final idUsuario = usuario.id;
+    final usuariosList =
+        await Provider.of<UsuariosViewModel>(context, listen: false)
+            .fetchUsuarios(idUsuario, 20, 0);
+    setState(() {
+      _usuario = usuario;
+      _usuariosList = usuariosList;
+    });
+  }
+
+  Future _fetchActividad(String? idUsuario) async {
+    idUsuario ??= Provider.of<LoginViewModel>(context, listen: false).idUsuario;
+    Provider.of<PrioridadesViewMode>(context, listen: false)
+        .fetchActividad(idUsuario);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    var vm = Provider.of<PrioridadesViewMode>(context);
+    var actividadDetalle = vm.actividadList;
+
+    var usuario = Provider.of<LoginViewModel>(context, listen: false);
+
+    final height = MediaQuery.of(context).size.height;
+
+    return SafeArea(
+      child: SingleChildScrollView(
+        child: Column(
+          children: [
+            Container(
+              padding: const EdgeInsets.all(10),
+              decoration: const BoxDecoration(
+                  color: Colors.white,
+                  borderRadius:
+                      BorderRadius.vertical(bottom: Radius.circular(15))),
+              child: Padding(
+                padding: const EdgeInsets.all(8.0),
+                child: Column(
+                    mainAxisAlignment: MainAxisAlignment.start,
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      const Text("Bienvenido ",
+                          style: TextStyle(
+                            fontSize: 25,
+                          )),
+                      Text(_usuario!.name! ?? usuario.name!,
+                          style: const TextStyle(
+                              fontSize: 45,
+                              fontWeight: FontWeight.bold,
+                              height: -1)),
+                      const SizedBox(height: 10),
+                      Container(
+                          decoration: BoxDecoration(
+                            borderRadius: BorderRadius.circular(10),
+                          ),
+                          child: AppDropdownSearch<Usuario>(
+                            controller: _usuariosController,
+                            itemAsString: (u) => u.name!,
+                            selectedItem: _usuario,
+                            compareFn: (item1, item2) {
+                              return item1.id == item2.id;
+                            },
+                            onChanged: (selectedUsuario) {
+                              if (selectedUsuario != null) {
+                                _fetchActividad(selectedUsuario.id);
+                              }
+                            },
+                            items: _usuariosList,
+                            etiqueta: "Actividades",
+                          )),
+                    ]),
+              ),
+            ),
+            const SizedBox(height: 10),
+            SizedBox(
+              width: double.infinity,
+              height: height * 0.9,
+              child: ListView.builder(
+                  itemCount: actividadDetalle.length,
+                  itemBuilder: (context, index) {
+                    var actividad = actividadDetalle[index];
+
+                    return _infoCard(actividad);
+                  }),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Card _infoCard(Actividad actividad) {
+    List<Widget> tareaWidgets = [];
+    for (var tarea in actividad.tareas!) {
+      final posicion = (actividad.tareas!.indexOf(tarea) + 1).toString();
+      tareaWidgets.add(
+        ListTile(
+          title: Text(tarea.contenido!),
+          subtitle: Text("Subtarea ${tarea.iops}"),
+        ),
+      );
+    }
+    return Card(
+      margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8),
+      elevation: 4,
+      shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(6.0)),
+      child: ExpansionTile(
+        backgroundColor: Colors.white24,
+        leading: const Icon(Icons.local_fire_department_rounded),
+        title: Text(
+          "${actividad.nombreProyecto!} - ${actividad.nombre!} ",
+          style: const TextStyle(
+              fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black54),
+          strutStyle: const StrutStyle(fontWeight: FontWeight.bold),
+        ),
+        trailing: const Icon(Icons.arrow_drop_down),
+        subtitle: Row(
+          mainAxisAlignment: MainAxisAlignment.start,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            Text(actividad.avance!),
+            const SizedBox(width: 10),
+            Expanded(
+              child: ClipRRect(
+                borderRadius: const BorderRadius.all(Radius.circular(10)),
+                child: LinearProgressIndicator(
+                    value: double.parse(actividad.avance!) / 100,
+                    backgroundColor: Colors.grey,
+                    valueColor: const AlwaysStoppedAnimation(AppTheme.primary)),
+              ),
+            ),
+          ],
+        ),
+        children: tareaWidgets,
+      ),
+    );
+  }
+}

+ 24 - 0
lib/views/profile/profile_screen.dart

@@ -0,0 +1,24 @@
+import 'package:flutter/material.dart';
+
+class ProfileScreen extends StatefulWidget {
+  static const String route = '/profile';
+
+  const ProfileScreen({super.key});
+
+  @override
+  State<ProfileScreen> createState() => _ProfileScreenState();
+}
+
+class _ProfileScreenState extends State<ProfileScreen> {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Perfil'),
+      ),
+      body: const Center(
+        child: Text('Perfil'),
+      ),
+    );
+  }
+}

+ 11 - 1
lib/widgets/app_drawer.dart

@@ -2,6 +2,8 @@
 
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
+import 'package:sis_flutter/data/session/session_storage.dart';
+import 'package:sis_flutter/services/usuario_session_service.dart';
 import 'package:sis_flutter/views/home/home_screen.dart';
 import '../models/usuario_model.dart';
 import '../themes/themes.dart';
@@ -11,6 +13,7 @@ import '../views/main/main_screen.dart';
 import 'widgets_components.dart';
 
 class AppDrawer extends StatelessWidget {
+  Usuario? usuario;
   AppDrawer({super.key});
 
   Future<bool> _showExitConfirmationDialog(BuildContext context) async {
@@ -42,10 +45,17 @@ class AppDrawer extends StatelessWidget {
     return shouldPop;
   }
 
+  Future<Usuario?> _loadUsuario() async {
+    final usuario = await getUsuarioFromSessionStorage();
+    return usuario;
+  }
+
   @override
   Widget build(BuildContext context) {
     String? nombre = Provider.of<LoginViewModel>(context).name.toString();
     String? correo = Provider.of<LoginViewModel>(context).email.toString();
+    _loadUsuario().then((value) => usuario = value);
+
     final avm = Provider.of<AdministracionViewModel>(context);
     List<String> permisos = avm.lospermisos;
     return Drawer(
@@ -74,7 +84,7 @@ class AppDrawer extends StatelessWidget {
                   height: 10,
                 ),
                 Text(
-                  nombre.toString(),
+                  usuario?.name ?? nombre.toString(),
                   style: const TextStyle(
                     color: Colors.white,
                     fontSize: 18,

+ 8 - 3
lib/widgets/app_dropdown_search.dart

@@ -1,8 +1,10 @@
+import 'dart:async';
+
 import 'package:dropdown_search/dropdown_search.dart';
 import 'package:flutter/material.dart';
 import '../themes/themes.dart';
 
-class AppDropdownSearch extends StatelessWidget {
+class AppDropdownSearch<T> extends StatelessWidget {
   final void Function(dynamic)? onChanged;
   final dynamic selectedItem;
   final String? etiqueta;
@@ -13,6 +15,7 @@ class AppDropdownSearch extends StatelessWidget {
   final List<dynamic> items;
   final String? Function(dynamic)? validator;
   final dynamic Function()? onPressedClear;
+  final DropdownSearchCompareFn? compareFn;
 
   const AppDropdownSearch({
     super.key,
@@ -26,6 +29,7 @@ class AppDropdownSearch extends StatelessWidget {
     this.validator,
     this.enabled = true,
     this.items = const [],
+    this.compareFn,
   });
 
   double _getFontSize(BuildContext context) {
@@ -53,17 +57,18 @@ class AppDropdownSearch extends StatelessWidget {
           ),
         if (etiqueta != null) const SizedBox(height: 5),
         DropdownSearch(
-          suffixProps: DropdownSuffixProps(
+          suffixProps: const DropdownSuffixProps(
             clearButtonProps: ClearButtonProps(
               isVisible: true,
               icon: const Icon(Icons.clear),
               iconSize: 20,
             ),
           ),
+          compareFn: compareFn,
           validator: validator,
           onChanged: onChanged,
           // asyncItems: asyncItems,
-          // items: items,
+          items: (filter, infiniteScrollProps) => items,
           itemAsString: itemAsString,
           selectedItem: selectedItem,
           enabled: enabled,

+ 10 - 5
lib/widgets/custom_bottom_navigation_bar.dart

@@ -4,14 +4,19 @@ import 'package:flutter/material.dart';
 import 'package:sis_flutter/themes/themes.dart';
 
 class CustomBottomNavigationBar extends StatelessWidget {
-  const CustomBottomNavigationBar({super.key});
+  final int indexValue;
+  final ValueChanged<int> onIndexSelected;
+  const CustomBottomNavigationBar(
+      {super.key, required this.indexValue, required this.onIndexSelected});
 
   @override
   Widget build(BuildContext context) {
     return BottomNavigationBar(
+      currentIndex: indexValue,
       backgroundColor: Colors.white,
       selectedItemColor: AppTheme.primary,
       unselectedItemColor: Colors.black87,
+      onTap: onIndexSelected,
       items: const [
         BottomNavigationBarItem(
           icon: Icon(
@@ -22,17 +27,17 @@ class CustomBottomNavigationBar extends StatelessWidget {
         ),
         BottomNavigationBarItem(
           icon: Icon(
-            Icons.calendar_today,
+            Icons.person,
             color: Colors.black87,
           ),
-          label: 'Calendario',
+          label: "Perfil",
         ),
         BottomNavigationBarItem(
           icon: Icon(
-            Icons.person,
+            Icons.calendar_today,
             color: Colors.black87,
           ),
-          label: "Perfil",
+          label: 'Calendario',
         ),
       ],
     );