This content originally appeared on DEV Community and was authored by Julio César Pérez Ortiz
Renderizar 600 elementos animados a 60 FPS en Flutter parece imposible, hasta que cambias tu enfoque: deja de crear y destruir widgets, empieza a reutilizarlos.
El Patrón: Object Pooling
Object Pooling es un patrón de diseño donde mantienes un conjunto fijo de objetos reutilizables en lugar de crearlos y destruirlos constantemente. En Flutter, esto significa:
Enfoque tradicional (ineficiente):
// Cada frame destruyes y recreas widgets
Widget build() {
return Stack(
children: List.generate(count, (i) => createNewParticle())
);
}
Enfoque con pooling (eficiente):
// Creas una vez, reutilizas infinitamente
void initState() {
particles = List.generate(count, (i) => Particle());
}
void update() {
if (particle.isOffScreen) {
particle.reset(); // Reutilizar, no destruir
}
}
Caso Real: Sistema de Lluvia
Para ilustrar este patrón, implementé un sistema de lluvia con 4 niveles de intensidad (100 a 600 gotas):
class _RainDropState extends State<_RainDrop> with SingleTickerProviderStateMixin {
late double dx, dy, vy;
late final Ticker _ticker;
@override
void initState() {
super.initState();
_randomizeValues();
_ticker = createTicker((elapsed) {
setState(() {
dy += vy;
if (dy >= widget.screenHeight + 100) {
_randomizeValues(); // Reset, no destruir
}
});
});
_ticker.start();
}
void _randomizeValues() {
dx = random.nextDouble() * widget.screenWidth;
dy = -500 - (random.nextDouble() * -500);
vy = rangeMap(z, 0, 20, minSpeed, maxSpeed);
}
}
¿Por Qué Funciona?
Eliminación de presión sobre el GC(Garbage Collector): No hay objetos temporales que limpiar.
Memoria predecible: La cantidad de widgets nunca cambia durante la ejecución.
CPU constante: El costo de actualizar es siempre el mismo, no importa cuánto tiempo pase.
Cache-friendly: Los objetos permanecen en memoria, mejor localidad de datos.
Más Allá de las Partículas: Patrones Relacionados
1. Flyweight Pattern: Compartir Datos Inmutables
En lugar de duplicar datos constantes, compártelos:
enum RainLevel {
ligera(count: 100, farSpeed: 10, nearSpeed: 4),
moderada(count: 250, farSpeed: 15, nearSpeed: 6),
torrencial(count: 600, farSpeed: 30, nearSpeed: 12);
final int count;
final double farSpeed;
final double nearSpeed;
const RainLevel({
required this.count,
required this.farSpeed,
required this.nearSpeed,
});
}
Cada gota accede a las mismas constantes en lugar de copiarlas.
2. Dirty Flag Pattern: Actualizar Solo lo Necesario
No reconstruyas todo el árbol de widgets si solo cambió un elemento:
AnimatedContainer(
duration: const Duration(seconds: 2),
curve: Curves.easeInOut,
decoration: BoxDecoration(gradient: currentGradient),
)
AnimatedContainer
solo interpola entre estados, no reconstruye el widget completo.
3. State Pattern: Comportamientos Intercambiables
Los gradientes de fondo son constantes precalculadas:
class ColorPalette {
static const Gradient dayGradient = LinearGradient(
colors: [skyBlue, lightSkyBlue],
);
static const Gradient nightGradient = LinearGradient(
colors: [midnightBlue, deepDarkBlue],
);
}
Gradient get backgroundGradient {
if (currentWeather == WeatherCondition.lluvioso) {
return ColorPalette.rainyGradient;
}
return _getTimeBasedGradient();
}
Cambiar de estado solo cambia la referencia, no recalcula nada.
4. Command Pattern: Animaciones Reutilizables
Una sola animación controla múltiples elementos:
_controller = AnimationController(
vsync: this,
duration: Duration(minutes: 5),
);
_controller.repeat();
// Usada por sol Y luna simultáneamente
CelestialPositions _calculatePositions(double progress) {
final angle = (progress * 2 * pi) - (pi / 2);
return CelestialPositions(
sunPosition: Offset(center.dx + radius * cos(angle)),
moonPosition: Offset(center.dx + radius * cos(angle + pi)),
);
}
Aplicaciones Más Allá del Clima
Este enfoque arquitectónico aplica a cualquier sistema con elementos múltiples y repetitivos:
Juegos
- Bullet pools: Reutilizar proyectiles en shooters
- Enemy spawners: Pool de enemigos que se reactivan
- Particle effects: Explosiones, humo, chispas
Visualización de Datos
- Real-time charts: Actualizar puntos sin recrear el gráfico
- Audio visualizers: Barras de ecualizador que cambian altura
- Network graphs: Nodos que actualizan posición
E-commerce
- Infinite scroll: Reutilizar cards al hacer scroll
- Carousels: Pool de items que rotan
- Loading skeletons: Placeholders que pulsan
Productividad
- Toast notifications: Pool de mensajes flotantes
- Drag & drop: Cursores y previsualizaciones reutilizables
- Canvas tools: Pinceles, formas, selecciones
El Principio Fundamental
Separar identidad de estado:
- La identidad (el widget/objeto) permanece constante
- El estado (posición, color, tamaño) cambia continuamente
// MAL: Mezclas identidad con estado
List<Widget> buildItems() {
return data.map((item) => ItemWidget(item)).toList();
}
// BIEN: Identidad fija, estado mutable
List<ItemWidget> items = List.generate(count, (i) => ItemWidget());
void updateItems(List<Data> newData) {
for (int i = 0; i < items.length; i++) {
items[i].updateData(newData[i]);
}
}
Cuándo Aplicar Este Patrón
Úsalo cuando:
- Tienes >50 elementos similares
- Los elementos aparecen/desaparecen frecuentemente
- El rendimiento es crítico (60 FPS)
- La estructura es repetitiva pero el contenido varía
Evítalo cuando:
- Tienes pocos elementos (<10)
- La estructura cambia radicalmente
- La lógica de reseteo es más compleja que recrear
- El costo de mantener el pool supera el beneficio
Resultados Medibles
Antes (recreación constante):
- 100 elementos: 30 FPS
- 250 elementos: 15 FPS
- Picos de GC cada 2-3 segundos
Después (object pooling):
- 600 elementos: 60 FPS constante
- Sin picos de GC perceptibles
- Uso de memoria estable
Conclusión
Object Pooling no es solo una optimización, es un cambio de mentalidad: piensa en tus widgets como actores reutilizables en un escenario, no como decorados desechables.
La próxima vez que necesites animar muchos elementos, pregúntate: ¿realmente necesito crear y destruir, o puedo simplemente resetear y reutilizar?
Proyecto Completo
El código completo de la aplicación de clima está disponible en GitHub:
App del Clima
Características implementadas:
- Object pooling para 600+ gotas de lluvia simultáneas
- 4 niveles de intensidad de lluvia (ligera, moderada, fuerte, torrencial)
- Sistema de transiciones suaves con AnimatedContainer
- Rotación celestial (sol/luna) con un solo AnimationController
- Integración con API REST para datos climáticos reales
- Arquitectura MVVM limpia y escalable
- Pronósticos por hora y diarios interactivos
¿Has aplicado object pooling en tus proyectos Flutter? ¿Qué otros patrones de optimización has encontrado útiles? Comparte tu experiencia en los comentarios.
This content originally appeared on DEV Community and was authored by Julio César Pérez Ortiz

Julio César Pérez Ortiz | Sciencx (2025-10-06T20:12:56+00:00) Object Pooling en Flutter: Cuando Reutilizar es Mejor que Recrear. Retrieved from https://www.scien.cx/2025/10/06/object-pooling-en-flutter-cuando-reutilizar-es-mejor-que-recrear/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.