Módulo 03: Dart Deep Dive

Closures

Programacion Funcional

# Closures en Dart Un **Closure** (cierre) es una función que tiene acceso al ámbito (scope) de su padre, incluso después de que la función padre haya terminado de ejecutarse. En términos simples: Una función que "recuerda" las variables del lugar donde fue creada. ## Anatomía de un Closure ```dart Function makeCounter() { // Variable local de makeCounter int count = 0; // Esta función anónima es el Closure // "Captura" la variable 'count' return () { count++; return count; }; } void main() { final counterA = makeCounter(); final counterB = makeCounter(); print(counterA()); // 1 print(counterA()); // 2 // counterB tiene su propia copia de 'count' print(counterB()); // 1 print(counterA()); // 3 } ``` Observa que `count` persiste entre llamadas a `counterA()`, aunque `makeCounter()` ya terminó su ejecución. ## Closures en Flutter Los Closures son omnipresentes en Flutter, a menudo sin que te des cuenta. ### 1. Callbacks y State ```dart class MyWidget extends StatelessWidget { final String title; MyWidget({required this.title}); @override Widget build(BuildContext context) { // Este callback es un closure que captura 'title' return ElevatedButton( onPressed: () { // Accede a 'title' del scope superior print('Button pressed: $title'); }, child: Text(title), ); } } ``` ### 2. Configuración de Handlers ```dart void main() { final users = ['Alice', 'Bob', 'Charlie']; final handlers = []; for (var user in users) { // Creamos un closure para cada usuario // Captura la variable 'user' de la iteración actual handlers.add(() => print('Hello $user')); } handlers[0](); // Hello Alice handlers[1](); // Hello Bob } ``` ⚠️ **Nota histórica:** En versiones antiguas de JavaScript, esto causaba errores porque la variable del loop era compartida. En Dart, cada iteración del `for-in` crea un nuevo scope para la variable, así que funciona como esperas. ## Contexto Léxico (Lexical Scoping) Dart usa "Lexical Scoping", lo que significa que el alcance de las variables se define por la estructura del código (dónde escribes las llaves `{}`), no por cuándo se ejecuta. ```dart var global = 'Global'; void main() { var mainVar = 'Main'; void outer() { var outerVar = 'Outer'; void inner() { // inner puede ver todas las variables hacia arriba print(global); print(mainVar); print(outerVar); } inner(); } outer(); } ``` ## Peligros de los Closures (Memory Leaks) Como un closure mantiene referencias a las variables que captura, puede causar fugas de memoria si no tienes cuidado. ```dart class HeavyObject { final data = List.filled(1000000, 'Data'); } void setupListener() { final heavy = HeavyObject(); // Este closure captura 'heavy' someLongLivedStream.listen((event) { print(heavy.data.length); }); // Mientras el stream esté activo, 'heavy' no puede ser // recolectado por el Garbage Collector, aunque setupListener termine. } ``` **Solución:** Cancela las suscripciones (`StreamSubscription.cancel()`) o limpia las referencias cuando ya no las necesites. ## Conclusión - Los Closures son funciones que "cierran sobre" (capturan) variables de su entorno. - Permiten encapsulamiento de estado (como el ejemplo del contador) sin usar clases. - Son fundamentales para el estilo de programación basado en callbacks de Flutter. - Ten cuidado con lo que capturas para evitar retener memoria innecesariamente.

Este apartado profundiza en los conceptos clave, proporcionando ejemplos prácticos y mejores prácticas para su aplicación en proyectos reales.

Al comprender estos detalles, podrás diseñar soluciones más robustas, mantenibles y escalables en tus aplicaciones Flutter y Dart.