RenderObject Tree es el tercer nivel de la jerarquía de Flutter, responsable del renderizado real de la interfaz en pantalla. Cada nodo de este árbol realiza el cálculo de tamaños (layout) y el dibujado (painting), convirtiendo la configuración de los widgets en píxeles. A diferencia del Widget Tree, el RenderObject Tree se crea una vez y se actualiza solo cuando cambia la geometría o la apariencia. Según la Referencia de la API de Flutter, 2025, la eficiencia del RenderObject Tree determina directamente la fluidez de las animaciones y la capacidad de respuesta de la aplicación.
Puntos clave
RenderObject Tree es una estructura jerárquica de objetos de renderizado que sirve como la representación real de la interfaz en memoria. Cada RenderObject conoce su propio tamaño, posición en pantalla y cómo dibujarse. A diferencia de los widgets, que son ligeros y pueden recrearse cientos de veces por segundo, un RenderObject es un objeto pesado con acceso directo al pipeline gráfico.
El camino del widget a los píxeles pasa por tres etapas: el Widget Tree describe lo que debe estar en pantalla; el Element Tree gestiona el ciclo de vida; el RenderObject Tree realiza el trabajo real. Cada RenderObjectWidget (como Padding, Transform, CustomPaint) crea un RenderObject que se añade al árbol de renderizado. RenderBox es el tipo más común de RenderObject, utilizado por el 99% de los widgets estándar.
RenderObject es una clase abstracta que define la interfaz para layout y paint. Contiene referencias al RenderObject padre y a los objetos hijos, así como métodos abstractos: performLayout, paint y hitTest. Cada RenderObject concreto implementa estos métodos según su comportamiento: RenderFlex distribuye el espacio entre los hijos, RenderImage muestra una imagen, RenderParagraph renderiza texto.
abstract class RenderObject {
RenderObject? parent;
Constraints constraints;
ParentData? parentData;
bool _needsLayout = true;
bool _needsPaint = true;
void performLayout();
void paint(PaintingContext context, Offset offset);
}
En esta estructura simplificada, RenderObject contiene los flags _needsLayout y _needsPaint, que marcan el objeto como necesitado de actualización. Cuando el Widget Tree cambia, el Element Tree marca los RenderObjects correspondientes como “sucios” para layout o paint, y en el siguiente frame Flutter realiza solo las operaciones necesarias.
Cada RenderObject realiza dos fases clave: layout (determinación de tamaños y posiciones) y paint (dibujado). Estas fases se ejecutan en orden estricto: primero layout para todo el árbol, luego paint. La fase de paint puede omitirse si el layout no ha cambiado, ahorrando recursos de la GPU.
Durante el layout, Flutter pasa restricciones (constraints) de arriba a abajo a través del RenderObject Tree. Cada padre establece un ancho y alto mínimo y máximo para su hijo. El hijo calcula su tamaño dentro de estas restricciones y lo devuelve al padre. Este proceso se llama “paso descendente” (pasar restricciones hacia abajo) y “paso ascendente” (devolver tamaños hacia arriba).
Después de completar el layout, Flutter realiza el paint — dibujado de cada RenderObject. El padre crea un PaintingContext, lo pasa a los objetos hijos y especifica el desplazamiento (offset). Cada RenderObject se dibuja en el Canvas utilizando primitivas gráficas: rectángulos, círculos, texto, imágenes y transformaciones. El Canvas interactúa directamente con Skia o Impeller.
Flutter utiliza un mecanismo de nodos “sucios” para minimizar el trabajo. Cuando solo cambia el tamaño de un RenderObject, Flutter no recalcula el layout de todo el árbol — marca el nodo cambiado y sus posibles ancestros hasta el RenderBox más cercano con tamaño fijo. De manera similar, cuando cambia la apariencia, solo se marca el paint sin rehacer el layout.
Flutter proporciona varias categorías de RenderObject para diferentes tareas: RenderBox para elementos rectangulares estándar, RenderSliver para áreas desplazables y RenderObjects personalizados para renderizado no estándar a través de CustomPainter. Cada tipo está optimizado para su rol en el árbol de renderizado.
RenderBox es la clase base para elementos de interfaz rectangulares. Sus subtipos incluyen: RenderPadding (añade relleno), RenderTransform (aplica transformaciones), RenderFlex (implementa Row y Column), RenderStack (superpone elementos), RenderImage (muestra imágenes), RenderParagraph (renderiza texto). Cada subtipo sobrescribe performLayout y paint para su propia lógica.
RenderSliver es un tipo de RenderObject para trabajar con áreas desplazables. A diferencia de RenderBox, Sliver utiliza SliverConstraints, que incluyen información del viewport. RenderSliverList y RenderSliverGrid crean RenderObjects solo para los elementos en el área visible, lo que permite manejar listas con millones de entradas.
Para gráficos no estándar, use CustomPaint y CustomPainter. CustomPaint crea RenderCustomPaint, que llama a los métodos paint de CustomPainter. Esto permite dibujar formas arbitrarias, gráficos y animaciones con control total sobre el Canvas. Según el equipo de Flutter, CustomPainter es más eficiente que los widgets estándar anidados para gráficos vectoriales complejos.
class CirclePainter extends CustomPainter {
final Color color;
CirclePainter({required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
size.width / 3,
paint,
);
}
@override
bool shouldRepaint(CirclePainter oldDelegate) =>
oldDelegate.color != color;
}
En este ejemplo, CirclePainter dibuja un círculo en el Canvas. El método shouldRepaint devuelve true solo cuando cambia el color, evitando repintados innecesarios. CustomPainter debe ser lo más ligero posible — realice todos los cálculos pesados fuera de los métodos paint.
RenderObject Tree se crea a partir del Element Tree mediante el mecanismo RenderObjectWidget. Cada RenderObjectWidget (Padding, Transform, CustomPaint) crea un RenderObjectElement, que a su vez crea y gestiona el RenderObject correspondiente. El elemento actúa como intermediario: pasa la configuración del widget al RenderObject y notifica al RenderObject de los cambios.
Cuando se monta un RenderObjectElement, llama al método createRenderObject de su widget. El widget crea una instancia de RenderObject y la devuelve al elemento. El elemento inserta el RenderObject en el RenderObject Tree llamando al método insertChildLayout del RenderObject padre. Este proceso ocurre solo en el primer montaje — en actualizaciones posteriores, el elemento simplemente actualiza los parámetros del RenderObject existente.
Cuando la configuración del widget cambia (por ejemplo, cambia el valor de padding), el elemento llama al método updateRenderObject, que pasa la nueva configuración al RenderObject existente. El RenderObject se marca como “sucio” para layout o paint, y en el siguiente frame el framework realiza las actualizaciones necesarias.
Cuando se desmonta un elemento, se llama al método unmount, que elimina el RenderObject del RenderObject Tree y libera recursos. Se llama a RenderObject.remove para desconectarlo del padre, tras lo cual el objeto puede ser recolectado por el recolector de basura. Flutter garantiza que ningún RenderObject permanezca en el árbol sin un elemento correspondiente.
RepaintBoundary es un widget que crea una capa separada para renderizar su contenido. Cuando el contenido dentro de un RepaintBoundary cambia, solo esa capa se repinta, mientras que el resto de la pantalla permanece sin cambios. RepaintBoundary es especialmente útil para animaciones, reproductores de vídeo, gráficos interactivos y otros elementos que se actualizan con frecuencia.
En el núcleo de RepaintBoundary se encuentra RenderRepaintBoundary — un RenderObject especial que crea un PictureLayer separado. En el primer renderizado, RenderRepaintBoundary registra los comandos gráficos en esta capa. En actualizaciones posteriores, si solo cambia el contenido dentro del RepaintBoundary, Flutter repinta solo esta capa en lugar de toda la pantalla. Las demás capas permanecen sin cambios y se reutilizan.
No todos los widgets necesitan RepaintBoundary. Úselo cuando una parte de la interfaz se actualiza con alta frecuencia (60 FPS o más) mientras el resto de la pantalla es estático. Ejemplos típicos: un indicador de carga animado, reproductor de vídeo, Canvas de juego, CustomPainter con repintados frecuentes. Para texto estático o botones, RepaintBoundary es excesivo y solo aumenta el consumo de memoria.
| Escenario | Recomendación | Motivo |
|---|---|---|
| Animación | Envolver en RepaintBoundary | Aísla el área que se actualiza con frecuencia |
| Lista desplazable | No es necesario | ListView usa capas automáticamente |
| Texto estático | No es necesario | Sin repintados frecuentes |
| CustomPainter | Recomendado | Repintados frecuentes de gráficos |
El rendimiento del RenderObject Tree depende del número de nodos, la profundidad del árbol y la frecuencia de repintado. Flutter DevTools (la pestaña “Rendering”) permite analizar el RenderObject Tree en tiempo real: cantidad de repintados, tiempo de layout y paint, número de capas y sus tamaños. El análisis regular ayuda a identificar cuellos de botella.
Overdraw es una situación en la que un solo píxel se dibuja varias veces por frame. Por ejemplo, cuando un widget semitransparente se superpone a otro, la GPU dibuja ambas capas. Use flags opacos (Container con color en lugar de decoration) para elementos no transparentes, de modo que Flutter omita las capas invisibles. Según el equipo de Flutter, reducir el overdraw puede disminuir el tiempo de paint hasta en un 30%.
Cada RepaintBoundary y algunos widgets (Opacity, ClipRRect, Transform) crean una capa separada (PictureLayer). Demasiadas capas aumentan el tiempo de composición. Use agrupación: en lugar de múltiples widgets Opacity en elementos individuales, aplique un solo Opacity al contenedor. En lugar de ClipRRect en cada elemento, use ClipRRect en el contenedor común.
Cuando un widget se declara como const, Flutter sabe que su configuración no cambiará y puede reutilizar el RenderObject correspondiente sin recrearlo. Los constructores const reducen la carga del recolector de basura y aceleran el primer frame. Use const para widgets con parámetros fijos: iconos, títulos, elementos decorativos.
const Text("Title", style: TextStyle(fontSize: 24));
const Icon(Icons.home, color: Colors.blue);
const SizedBox.shrink();
Preguntas frecuentes
Widget Tree es una configuración ligera de la interfaz que se recrea en cada reconstrucción. RenderObject Tree es un árbol pesado de objetos de renderizado que persiste y se actualiza solo cuando cambia la geometría o la apariencia.
Use Flutter DevTools — la pestaña “Rendering”. Verá las capas de renderizado, los tiempos de layout y paint, e información detallada de cada RenderObject: tamaños, restricciones y flags sucios.
Dirty es un RenderObject marcado como necesitado de actualización. El flag _needsLayout o _needsPaint se establece cuando cambia la configuración, y Flutter realiza el layout o paint para ese nodo en el siguiente frame.
Sí, cree una subclase de RenderBox y sobrescriba los métodos performLayout y paint. Use RenderObjectWidget para incrustar un RenderObject personalizado en el Widget Tree. Esta es una técnica avanzada para renderizado no estándar.
RepaintBoundary crea RenderRepaintBoundary, que aísla parte del RenderObject Tree en una capa separada. Cuando el contenido cambia, solo esa capa se repinta, dejando el resto del árbol sin cambios.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también