# 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.