Menu
Coddy logo textTech

Understanding Closures

Part of the Logic & Flow section of Coddy's Dart journey — lesson 46 of 65.

A closure is a special type of function that "remembers" variables from the place where it was created, even after that original context has finished running. Think of it as a function that carries a piece of its birthplace with it wherever it goes.

When you create a function inside another function, the inner function has access to the outer function's variables. What makes this a closure is that this access persists even after the outer function has completed its execution.

Function createGreeting(String name) {
  return () {
    return "Hello, $name!";
  };
}

In this example, the anonymous function returned by createGreeting is a closure. It captures the name parameter from its surrounding scope. Even after createGreeting finishes running, the returned function still remembers what name was.

Closures are incredibly useful for creating specialized functions or maintaining state in a clean way. They allow you to build functions that are customized based on the environment where they were created, making your code more flexible and powerful.

challenge icon

Challenge

Easy

Create a program that demonstrates closures by building a personalized message generator system. Your program will create functions that "remember" specific information and use that information to generate customized messages even after the original context has finished.

  1. Read a string input representing a person's name
  2. Read a string input representing a greeting type ("formal", "casual", or "friendly")
  3. Read a string input representing a company name
  4. Create a function called createMessageGenerator that takes a name and greeting type as parameters
  5. The createMessageGenerator function should return another function (a closure) that:
    • Takes a company name as its parameter
    • Uses the name and greeting type from the outer function's scope
    • Returns a formatted message based on the greeting type:
      • For "formal": "Dear [name], we are pleased to welcome you to [company]."
      • For "casual": "Hey [name]! Welcome to [company]!"
      • For "friendly": "Hi [name], so glad you joined [company]!"
  6. Call the createMessageGenerator function with the name and greeting type inputs to get your closure function
  7. Call the returned closure function with the company name input to generate the message
  8. Display the results in the exact format shown below

For example, if the inputs are "Alice", "formal", and "TechCorp", your program should output:

Message Generator System
========================
Creating message generator for: Alice
Greeting type: formal
Company: TechCorp
========================
Generated message: Dear Alice, we are pleased to welcome you to TechCorp.
Message generation completed successfully

If the inputs are "Bob", "casual", and "StartupXYZ", your program should output:

Message Generator System
========================
Creating message generator for: Bob
Greeting type: casual
Company: StartupXYZ
========================
Generated message: Hey Bob! Welcome to StartupXYZ!
Message generation completed successfully

If the inputs are "Carol", "friendly", and "InnovateLab", your program should output:

Message Generator System
========================
Creating message generator for: Carol
Greeting type: friendly
Company: InnovateLab
========================
Generated message: Hi Carol, so glad you joined InnovateLab!
Message generation completed successfully

Your program must demonstrate closures by creating a function that returns another function. The returned function (closure) should have access to the variables from the outer function's scope (name and greeting type) even after the outer function has finished executing. Use conditional statements to determine the appropriate message format based on the greeting type input.

Cheat sheet

A closure is a function that "remembers" variables from the place where it was created, even after that original context has finished running.

When you create a function inside another function, the inner function has access to the outer function's variables. This access persists even after the outer function has completed its execution.

Function createGreeting(String name) {
  return () {
    return "Hello, $name!";
  };
}

In this example, the anonymous function returned by createGreeting is a closure. It captures the name parameter from its surrounding scope. Even after createGreeting finishes running, the returned function still remembers what name was.

Closures are useful for creating specialized functions or maintaining state in a clean way. They allow you to build functions that are customized based on the environment where they were created.

Try it yourself

import 'dart:io';

void main() {
  // Read input
  String? name = stdin.readLineSync();
  String? greetingType = stdin.readLineSync();
  String? company = stdin.readLineSync();
  
  // Display system header
  print("Message Generator System");
  print("========================");
  print("Creating message generator for: $name");
  print("Greeting type: $greetingType");
  print("Company: $company");
  print("========================");
  
  // TODO: Write your code below
  // Create the createMessageGenerator function that returns a closure
  // Call the function and generate the message
  
  // Output the final results
  print("Generated message: \$message");
  print("Message generation completed successfully");
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Logic & Flow