What is a Closure?
A closure is a function that captures variables from its surrounding lexical scope. In other words, a closure 'remembers' the environment in which it was created, even after that environment is no longer active. This allows the function to access and modify those captured variables, creating functions with private state.
In Dart, every function is a closure because functions can access variables defined in their outer scope. However, the term is often used specifically for anonymous functions that outlive the scope where they were defined (e.g., when returned from another function).
Lexical Scoping
Before understanding closures, you need to understand lexical scoping. In Dart, the scope of a variable is determined by where it is declared in the code. Inner functions can access variables from outer functions.
Creating a Simple Closure
When you return an inner function, it continues to have access to the outer function's variables, even after the outer function has finished executing. That returned function is a closure.
Each call to makeCounter creates a new closure with its own count variable. The closures keep the variable alive as long as they exist.
How Closures Work
When a function is created, it captures a reference to the variables it uses from the surrounding scopes. These variables are stored in a special context object that persists as long as the closure exists. This is why closures can have private state.
Closures in Callbacks
Closures are extremely useful in asynchronous programming and event handlers, where you want to remember some state for later.
If you didn't capture i correctly in a loop, you might get unexpected behavior. Closures capture variables by reference, so you need to be careful with loop variables.
Common Pitfall: Capturing Loop Variables
A classic mistake is creating closures inside a loop that capture the loop variable. By the time the closure is executed, the loop variable may have changed.
The solution is to create a new variable inside the loop that holds the current value.
Closures and Asynchronous Code
Closures are essential in async programming, especially with Future.then or Stream.listen. They capture the necessary state to handle the result later.
Key Takeaways
- A closure is a function that captures variables from its lexical scope.
- Closures enable functions to have private state that persists between calls.
- Each closure instance has its own copy of captured variables.
- Be careful when capturing loop variables – create a new local variable to capture the current value.
- Closures are heavily used in callbacks, event handlers, and functional programming.