Un Fragment kotlin permite construir una parte de la interfaz de una aplicación Android como una unidad reutilizable, con su propio ciclo de vida y una vista independiente. Se usa para organizar pantallas, adaptar diseños a distintos tamaños y separar responsabilidades, pero su comportamiento puede resultar confuso si se trata como una Activity pequeña. La clave está en entender cuándo existe el Fragment, cuándo existe su vista y cómo coordinar ambos ciclos sin perder datos ni conservar referencias inválidas.
Cuándo conviene dividir una pantalla en Fragments
Un Fragment es adecuado cuando una pantalla necesita secciones con responsabilidades diferenciadas o cuando una parte de la interfaz puede reutilizarse en más de un flujo. Por ejemplo, una aplicación de reservas puede tener una Activity que aloja un Fragment de búsqueda y, tras seleccionar una fecha, mostrar otro Fragment con los resultados. En una tableta, esas mismas áreas podrían mostrarse simultáneamente, mientras que en un teléfono se presentan de forma secuencial.
La división también ayuda a que la navegación no dependa de una única Activity llena de condiciones. Cada Fragment puede representar un destino con su propio diseño y estado de interfaz. Sin embargo, crear uno para cada grupo de vistas no mejora automáticamente la arquitectura. Si dos componentes siempre se muestran juntos, no se reutilizan y no tienen comportamiento independiente, dividirlos puede añadir transacciones, comunicación y estados que no aportan valor.
Como criterio práctico, conviene crear un Fragment cuando exista una frontera clara: una sección navegable, una función reutilizable o una región que deba adaptarse de manera distinta según el tamaño de pantalla. Para una interfaz sencilla con una única pantalla, una Activity puede ser suficiente. La decisión debe responder a la estructura y evolución previstas, no a la idea de que cada vista necesita su propio Fragment.
Crear el Fragment y conectar su diseño
En una aplicación basada en vistas, un Fragment suele extender Fragment y devolver su interfaz desde onCreateView. Un ejemplo mínimo puede expresarse así:
class SearchFragment : Fragment(R.layout.fragment_search)
Al proporcionar el recurso de diseño en el constructor, la biblioteca se encarga de inflar ese layout. Para diseños más complejos también se puede sobrescribir onCreateView e inflar la vista manualmente. En ambos casos, el contenedor de la Activity debe tener un espacio donde alojar el Fragment; en proyectos actuales se suele utilizar FragmentContainerView.
El Fragment no aparece por el mero hecho de estar declarado en el código. Debe añadirse mediante el sistema de navegación o una transacción del administrador de Fragments. En una configuración sencilla, una transacción puede reemplazar el contenido del contenedor por el nuevo destino. Para una aplicación con varias pantallas y acciones de navegación, Navigation Component suele ofrecer un modelo más explícito: define destinos, acciones y una pila de retroceso sin repartir esa lógica entre muchas transacciones manuales.
Es recomendable que la Activity contenedora se limite a alojar la navegación y a coordinar elementos realmente compartidos. Si acaba decidiendo el contenido de cada pantalla, gestionando formularios y almacenando datos de todos los Fragments, la separación ha quedado en el nombre, no en la arquitectura.
El ciclo de vida: la diferencia que evita errores
Un Fragment tiene un ciclo de vida propio y, cuando crea una interfaz, la vista tiene otro. Esta distinción explica muchos errores habituales. El objeto Fragment puede seguir existiendo después de que su vista haya sido destruida; por ello, una referencia a un botón o a un binding que pertenecía a la vista anterior no debe conservarse para usarla más tarde.
Los métodos más relevantes incluyen onCreate, para preparar datos y estado no ligado a la vista; onCreateView o el constructor con layout, para crear la interfaz; y onViewCreated, para configurar listeners, observar datos y preparar componentes visuales. Cuando la vista deja de existir, onDestroyView es el lugar para liberar referencias a ella.
Con View Binding, por ejemplo, se suele guardar el binding en una propiedad anulable y establecerlo en nulo en onDestroyView. Así se evita que el Fragment mantenga viva una vista que ya no está en pantalla. Este cuidado es especialmente relevante al navegar entre destinos, rotar el dispositivo o volver a una pantalla conservada en la pila.
El estado duradero tampoco debería depender únicamente de las propiedades de la vista. Un campo de texto puede perder su contenido si el proceso se recrea o si la pantalla se reconstruye. Para datos de interfaz que deban sobrevivir a cambios de configuración, resulta apropiado un ViewModel. Si el dato debe persistir más allá de la pantalla, puede ser necesario guardarlo en un repositorio o almacenamiento local. Un ViewModel no reemplaza a una base de datos ni convierte automáticamente cualquier dato en persistente.
Comunicar Fragments sin crear dependencias frágiles
Una pantalla necesita a menudo enviar una selección a otra. La solución rápida suele ser buscar el Fragment vecino, convertirlo a su clase concreta y llamar directamente a uno de sus métodos. Este patrón acopla las dos pantallas: si cambia la navegación o se reutiliza una de ellas, también hay que modificar la otra.
Para compartir información entre destinos, una opción común es utilizar el back stack de navegación y un ViewModel con ámbito adecuado. En flujos sencillos, también se pueden emplear los Fragment Result APIs, pensadas para enviar resultados entre Fragments sin mantener una referencia directa al receptor. Si el dato pertenece a toda la actividad, un ViewModel compartido con ámbito de Activity puede ser razonable, aunque no conviene convertirlo en un almacén global de todo el estado de la aplicación.
Por ejemplo, al elegir un filtro, el Fragment de resultados puede exponer ese estado mediante un ViewModel y observar los cambios. El Fragment que presenta los filtros modifica el estado, pero no necesita conocer cómo se dibuja la lista. Así, cada componente conserva una responsabilidad clara. La comunicación debe transportar datos o eventos concretos; no debería convertirse en una cadena de llamadas que reproduzca la estructura interna de todas las pantallas.
Errores frecuentes y una revisión útil antes de publicar
Un problema típico es iniciar operaciones de interfaz con el ciclo de vida equivocado. Una observación ligada al Fragment puede seguir activa cuando su vista ya no está disponible. En pantallas que usan componentes de AndroidX, se recomienda observar datos con el propietario de ciclo de vida de la vista, normalmente viewLifecycleOwner. De este modo, la observación se detiene cuando se destruye la interfaz y se reactiva cuando corresponde.
Otro fallo es ejecutar transacciones cuando el estado de la Activity ya se ha guardado. En ese momento, modificar la pila puede provocar excepciones o dejar una navegación difícil de restaurar. La solución no es ocultar el problema usando indiscriminadamente transacciones que permitan perder estado; es coordinar el evento con el ciclo de vida y evitar que acciones tardías intenten cambiar una pantalla que ya no está en un estado válido.
- No guardar vistas en un ViewModel: el ViewModel debe contener estado y lógica de presentación, no referencias a una Activity, un Fragment, un binding o una vista.
- No usar el contexto sin revisar su duración: una referencia prolongada a una Activity puede causar fugas de memoria. Utiliza el contexto apropiado para la tarea y no almacenes el contexto de la interfaz sin necesidad.
- No confundir restauración con persistencia: los mecanismos de estado ayudan a reconstruir una pantalla, pero no sustituyen el almacenamiento de información que deba sobrevivir a cierres y reinicios.
- No añadir transacciones sin una estrategia de navegación: duplicar destinos o gestionar manualmente la pila sin una regla clara puede hacer que el botón Atrás produzca resultados inesperados.
Antes de publicar, conviene probar la pantalla al rotar el dispositivo, navegar hacia delante y volver, recrear la Activity y cambiar entre estados vacíos, cargando y error. También hay que comprobar qué ocurre cuando una petición termina después de abandonar el Fragment. Estas pruebas suelen revelar problemas de ciclo de vida que no aparecen durante una ejecución lineal.
Elegir entre Fragments y otras opciones de interfaz
Los Fragments siguen siendo una solución válida para interfaces Android basadas en vistas y para aplicaciones que ya utilizan Navigation Component. También resultan útiles cuando una misma sección debe integrarse en distintas Activities o composiciones. Su coste está en la complejidad del ciclo de vida y en la necesidad de coordinar vistas, estado y navegación con cuidado.
En proyectos nuevos con Jetpack Compose, una pantalla puede construirse con funciones composables y un sistema de navegación compatible, sin introducir un Fragment para cada destino. Compose no elimina la necesidad de gestionar estado o navegación; cambia las herramientas y el modelo de interfaz. En aplicaciones existentes, una migración gradual puede combinar Fragments y Compose, por ejemplo, incorporando una vista Compose dentro de un Fragment. La elección depende del código actual, la experiencia del equipo, las bibliotecas utilizadas y el alcance real de la migración.
Para empezar con un Fragment kotlin, define primero qué parte de la aplicación representa y cómo se navega hasta ella. Después, separa el estado de la vista, utiliza el ciclo de vida de esta para observar datos y selecciona un mecanismo de comunicación que no ate unas pantallas a otras. Si cada Fragment tiene una responsabilidad identificable y puede recrearse sin perder el control del estado, la arquitectura será más fácil de mantener y ampliar.
