Watch the video (16 min)
On this page25 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. The Arrow Shortcut
  5. 2. Reading an Arrow Function
  6. 3. Arrow Functions That Return Nothing
  7. 4. Only One Expression
  8. 5. Choices Inside an Arrow
  9. 6. Functions Without a Name
  10. 7. Storing a Function in a Variable
  11. 8. Anonymous Arrow Functions
  12. 9. Dart Still Checks Every Call
  13. 10. Swapping the Function
  14. 11. What dart analyze Says
  15. 12. Passing a Function Directly
  16. 13. A Real-World Example
  17. Code Examples
  18. Common Beginner Mistakes
  19. Best Practices
  20. Mini Challenge
  21. Practice Questions
  22. Practice Answers
  23. Quick Revision
  24. Key Takeaways
  25. What's Next?

Lecture 34 — Arrow & Anonymous Functions

Category6 — Functions
Lecture034 of 140
Estimated video length16 min
TargetDart 3.12.2
LevelBeginner

Three lines of braces and return just to give back n * n is a lot of ceremony. Arrow functions shrink a one-expression function to one line, and anonymous functions let you create a function without even giving it a name.

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways

Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.


Prerequisites

Students should already understand, from Lectures 001–033:

  • conditional expressions ? : (Lecture 020) and switch expressions with => arms (Lecture 025)
  • for-in loops over a group of values in square brackets, like [85, 92, 78] (Lecture 027)
  • defining and calling functions with typed parameters and a return type (Lectures 030–031)
  • return, and why every path of a non-void function must return a value

Connection With Previous Lectures

Lecture 031 ended with many small functions like these:

dart
int square(int n) {
  return n * n;
}

bool isAdult(int age) {
  return age >= 18;
}

Each one does a single calculation, yet takes three lines: braces, the word return, and a semicolon around one small expression. A program with twenty such helpers is mostly braces. And every function so far needed a name, even one we planned to use exactly once. This lecture removes both kinds of ceremony.


Learning Objectives

By the end of this lecture you will be able to:

  • Write a function body with the arrow => instead of braces and return
  • Explain why an arrow body must be a single expression
  • Use ? : and switch expressions to make choices inside an arrow body
  • Create an anonymous function and store it in a variable
  • Call a function stored in a variable, and recognize the checks Dart still applies
  • Replace the function inside a variable with another of the same type
  • Pass an anonymous function directly to forEach
  • Choose between braces, an arrow, and an anonymous function

1. The Arrow Shortcut

When a function's body is just return plus one expression, you can write => and the expression instead.

dart
int square(int n) => n * n;

void main() {
  print(square(4));
}
output
16

=> n * n; means exactly the same as { return n * n; }. The function still has a name, a parameter, and a return type — only the body is shorter. This is called an arrow function.

Analogy — the arrow is like a sign pointing to the answer: "square(n) → go to n * n". You met the same sign in Lecture 025, where a switch arm 1 => 'Monday' pointed to its result.


2. Reading an Arrow Function

Long formArrow form
int square(int n) { return n * n; }int square(int n) => n * n;
bool isAdult(int age) { return age >= 18; }bool isAdult(int age) => age >= 18;

Three things change: the braces go, the word return goes, and => takes their place. The semicolon stays — it now ends the whole function.

dart
bool isAdult(int age) => age >= 18;

String fullName(String first, String last) => '$first $last';

double average(int total, int count) => total / count;

void main() {
  print(isAdult(20));
  print(fullName('Sara', 'Khan'));
  print(average(240, 3));
}
output
true
Sara Khan
80.0

3. Arrow Functions That Return Nothing

A void function can use an arrow too, when its whole job is one call:

dart
void greet(String name) => print('Hello, $name!');

void main() {
  greet('Ali');
  greet('Zayan');
}
output
Hello, Ali!
Hello, Zayan!

Nothing is given back; => here simply means "the body is this one expression".


4. Only One Expression

An arrow body must be a single expression — something that produces a value. Statements such as return or if are not allowed after =>:

dart
int square(int n) => return n * n; // ✗

void main() {
  print(square(3));
}
output
Error: Unexpected token 'return'.
int square(int n) => return n * n; // ✗
                     ^^^^^^

The arrow already means "return", so the word is not needed. An if fails too, with several errors starting with Expected an identifier, but got 'if'.


5. Choices Inside an Arrow

You can still make a choice — with the expression forms of choosing. Use ? : for two options (Lecture 020) and a switch expression for more (Lecture 025):

dart
String result(int marks) => marks >= 50 ? 'Pass' : 'Fail';

String grade(int marks) => switch (marks) {
  >= 80 => 'A',
  >= 60 => 'B',
  _ => 'C',
};

void main() {
  print(result(72));
  print(grade(85));
  print(grade(40));
}
output
Pass
A
C

If a function needs several statements — a variable, a loop, an early return — keep the braces. The arrow is for functions that are one expression naturally.


6. Functions Without a Name

Every function so far had a name. An anonymous function leaves the name out: just the parameters and a body.

dart
(int a, int b) {
  return a + b;
}

On its own this does nothing — like writing a number on a line without storing it. To use an anonymous function, you either store it in a variable or hand it straight to something that will call it. Anonymous functions are also called lambdas in many languages.


7. Storing a Function in a Variable

Put the anonymous function on the right side of =. Note the semicolon after the closing brace — the whole thing is one variable declaration.

dart
void main() {
  var add = (int a, int b) {
    return a + b;
  };

  print(add(2, 3));
  print(add(10, 20));
}
output
5
30

You call the variable with parentheses, exactly like a named function. add holds a function, and add(2, 3) runs it.


8. Anonymous Arrow Functions

An anonymous function can use an arrow body too. A function with no parameters keeps empty parentheses:

dart
void main() {
  var square = (int n) => n * n;
  var sayHi = () => print('Hi!');

  print(square(5));
  sayHi();
}
output
25
Hi!

(int n) => n * n reads: "a function that takes an int called n and gives back n * n".


9. Dart Still Checks Every Call

A function in a variable is just as strict as a named one. The types of its parameters and its result are inferred from the anonymous function:

dart
void main() {
  var square = (int n) => n * n;
  print(square.runtimeType);
  print(square('3')); // ✗
}
output
Error: The argument type 'String' can't be assigned to the parameter type 'int'.
  print(square('3')); // ✗
               ^

Remove the broken line and the program prints (int) => int: a function that takes an int and returns an int. Function types like this — and writing them yourself — are the topic of Lecture 035.


10. Swapping the Function

Why store a function in a variable instead of naming it? Because a variable can change. You can put a different function into it, as long as the types match:

dart
void main() {
  var greet = (String name) => 'Hello, $name!';
  print(greet('Ali'));

  greet = (String name) => 'Salaam, $name!';
  print(greet('Ali'));
}
output
Hello, Ali!
Salaam, Ali!

The same call, greet('Ali'), now runs a different function. A function with different types does not fit:

dart
void main() {
  var greet = (String name) => 'Hello, $name!';
  greet = (int n) => 'Number $n'; // ✗
  print(greet('Ali'));
}
output
Error: A value of type 'String Function(int)' can't be assigned to a variable of type 'String Function(String)'.
  greet = (int n) => 'Number $n'; // ✗
          ^

11. What dart analyze Says

If a variable gets one function and never changes, dart analyze suggests a normal declaration instead. For var square = (int n) => n * n; it prints:

output
info - Use a function declaration rather than a variable assignment to bind a function to a name. Try rewriting the closure assignment as a function declaration. - prefer_function_declarations_over_variables

This is style advice, not an error — the program runs. The fix is the arrow function from Step 1: int square(int n) => n * n;. Store a function in a variable when it really varies, as in Step 10; Lecture 035 shows more reasons to treat functions as values.


12. Passing a Function Directly

The most common home of an anonymous function is as an argument. A group of values in square brackets has a forEach method, which calls your function once for each value:

dart
void main() {
  ['Ali', 'Sara', 'Zayan'].forEach((name) {
    print('Hello, $name!');
  });
}
output
Hello, Ali!
Hello, Sara!
Hello, Zayan!

The function has no name and is never stored — it is written right where it is needed. forEach hands it 'Ali', then 'Sara', then 'Zayan'. Dart even infers that name is a String, so the type can be left out here. With an arrow:

dart
void main() {
  [85, 92, 78].forEach((mark) => print('Mark: $mark'));
}
output
Mark: 85
Mark: 92
Mark: 78

For a job this simple, the for-in loop from Lecture 027 is clearer, and dart analyze says so with Function literals shouldn't be passed to 'forEach'. Try using a 'for' loop. We use forEach only as the first glimpse of passing a function. Functions that take other functions are the topic of Lecture 036, and lists themselves begin in Lecture 039.


13. A Real-World Example

dart
double lineTotal(double price, int quantity) => price * quantity;

String money(double amount) => 'Rs ${amount.toStringAsFixed(2)}';

void main() {
  var discount = (double amount) => 0.0;
  double total = lineTotal(250, 3) + lineTotal(40, 5);
  print('Total: ${money(total)}');
  print('Discount: ${money(discount(total))}');

  // Sale day: swap in a new discount rule
  discount = (double amount) => amount >= 500 ? amount * 0.10 : 0.0;
  print('Sale discount: ${money(discount(total))}');
  print('To pay: ${money(total - discount(total))}');
}
output
Total: Rs 950.00
Discount: Rs 0.00
Sale discount: Rs 95.00
To pay: Rs 855.00

lineTotal and money are fixed one-expression jobs, so they are arrow functions. The discount rule changes on sale days, so it lives in a variable holding an anonymous function, and the rest of the program calls discount(total) without caring which rule is inside.


Code Examples

Example 1 — An Arrow Function

dart
int cube(int n) => n * n * n;

void main() {
  print(cube(3));
}
output
27

Example 2 — Arrow Returning a bool

dart
bool isEven(int n) => n % 2 == 0;

void main() {
  print(isEven(10));
  print(isEven(7));
}
output
true
false

Example 3 — A void Arrow Function

dart
void line() => print('----------');

void main() {
  line();
  print('Report');
  line();
}
output
----------
Report
----------

Example 4 — A Choice in an Arrow

dart
String status(bool online) => online ? 'Online' : 'Offline';

void main() {
  print(status(true));
  print(status(false));
}
output
Online
Offline

Example 5 — Swapping a Stored Function

dart
void main() {
  var format = (String name) => name.toUpperCase();
  print(format('Sara'));
  format = (String name) => name.toLowerCase();
  print(format('Sara'));
}
output
SARA
sara

Example 6 — An Anonymous Function as an Argument

dart
void main() {
  ['Pen', 'Notebook', 'Bag'].forEach((item) => print('- $item'));
}
output
- Pen
- Notebook
- Bag

Example 7 — Student Marks

dart
double average(int total, int subjects) => total / subjects;

String grade(double avg) => switch (avg) {
  >= 80 => 'A',
  >= 60 => 'B',
  _ => 'C',
};

void main() {
  double avg = average(80 + 75 + 85, 3);
  print('Average: $avg, Grade: ${grade(avg)}');
}
output
Average: 80.0, Grade: A

Example 8 — Shopping Total

dart
double withTax(double price) => price * 1.05;

void main() {
  double total = 0;
  [200.0, 100.0, 50.0].forEach((price) {
    total += withTax(price);
  });
  print('Total with tax: $total');
}
output
Total with tax: 367.5

How it works

  1. forEach calls the anonymous function once for each price.
  2. Inside it, the arrow function withTax adds 5%.
  3. The anonymous function can update total, a variable of main. Why it can see total is explained in Lecture 037 — Closures & Scope.

Example 9 — Bank Balance

dart
void main() {
  var fee = (int amount) => 0;
  int balance = 1000;
  balance -= 300 + fee(300);
  print('Balance: $balance');

  fee = (int amount) => amount ~/ 100;
  balance -= 300 + fee(300);
  print('Balance: $balance');
}
output
Balance: 700
Balance: 397

How it works

  1. At first the fee rule returns 0, so withdrawing 300 costs 300.
  2. Then a new rule is stored: 1 rupee per 100 withdrawn, so the next 300 costs 303.
  3. The withdrawal lines are identical — only the function in fee changed.

Example 10 — Temperature Conversion

dart
double toFahrenheit(double c) => c * 9 / 5 + 32;

String describe(double c) => c >= 30 ? 'hot' : 'pleasant';

void main() {
  [34.0, 22.0].forEach((c) {
    print('$c°C = ${toFahrenheit(c)}°F, ${describe(c)}');
  });
}
output
34.0°C = 93.2°F, hot
22.0°C = 71.6°F, pleasant

Common Beginner Mistakes

1. Writing return after the arrow

dart
// int square(int n) => return n * n;
output
Error: Unexpected token 'return'.

The arrow already returns. Write int square(int n) => n * n;.

2. Putting braces after the arrow

dart
// int square(int n) => {n * n};
output
Error: A value of type 'Set<int>' can't be returned from a function with return type 'int'.

After =>, braces don't make a block — they make a Set, a kind of collection (Lecture 047). Use the arrow or braces, never both. With void, => {print('Hi')}; even compiles, and only dart analyze warns: Braces unnecessarily wrap this expression in a set literal.

3. Forgetting the semicolon after a stored function

dart
// var add = (int a, int b) {
//   return a + b;
// }
output
Error: Expected ';' after this.

The declaration ends after the closing brace, so it needs };.

4. Forgetting the parentheses when calling

dart
void main() {
  var sayHi = () => print('Hi!');
  sayHi; // ✗ mentions the function, does not call it
  print('end');
}
output
end

No error and no Hi!. sayHi is the function itself; sayHi() runs it.

5. Leaving out parameter types in a stored function

dart
void main() {
  var add = (a, b) => a + b;
  print(add.runtimeType);
}
output
(dynamic, dynamic) => dynamic

With no types to go on, a and b become dynamic (Lecture 011), so add('Ali', 5) compiles and then crashes while running. Write (int a, int b) => a + b. Inside forEach the types are inferred, so they may be left out there.

6. Forcing several statements into an arrow

=> if ... and => return ... do not compile. Use ? : or a switch expression, or go back to braces.


Best Practices

  • Use an arrow when the body is one short expression; use braces for anything longer.
  • Keep arrow functions readable on one line — if it wraps awkwardly, use braces.
  • Give a fixed, reused job a name: int square(int n) => n * n;.
  • Store a function in a variable only when the function itself may change.
  • Always type the parameters of a stored anonymous function.
  • For a simple "do this for each value", prefer a for-in loop over forEach.

Mini Challenge

Write an arrow function int bonus(int marks) that returns 10 when marks are 90 or more and 0 otherwise. Then store an anonymous function report that takes a String name and an int mark and prints <name>: <mark + bonus>. Call report for Ali (95) and Zayan (70).

Your program should print exactly:

output
Ali: 105
Zayan: 70

Try it yourself before opening the solution.

Show solution
dart
int bonus(int marks) => marks >= 90 ? 10 : 0;

void main() {
  var report = (String name, int marks) =>
      print('$name: ${marks + bonus(marks)}');
  report('Ali', 95);
  report('Zayan', 70);
}
output
Ali: 105
Zayan: 70

How it works

  1. bonus is one expression, ? :, so it is an arrow function.
  2. report is an anonymous arrow function stored in a variable and called twice.
  3. dart analyze suggests making report a declaration — try it: void report(...) => ....

Practice Questions

Work through these in order — they run from beginner to application to challenge. Every question gives you exact values, so you can compare your output with the answers.

Beginner

  1. Rewrite int twice(int n) { return n * 2; } as an arrow function. Print twice(21).
  2. Write an arrow function bool isPositive(int n). Print isPositive(-4).
  3. Write a void arrow function welcome(String name) that prints Welcome, <name>!. Call it with 'Sara'.

Application

  1. Store an anonymous function in a variable area that takes int width and int height and returns their product. Print area(12, 5).
  2. Write an arrow function String size(int n) that returns 'big' when n is over 100 and 'small' otherwise. Print size(250) and size(8).
  3. Use forEach with an anonymous arrow function to print Hi, <name> for 'Ali', 'Sara' and 'Zayan'.
  4. Store var message = (String name) => 'Good morning, $name';, print it for 'Ali', then swap in a function returning 'Good night, $name' and print it for 'Ali' again.
  5. Write String day(int n) as an arrow function with a switch expression: 1 → 'Mon', 2 → 'Tue', anything else → '?'. Print day(2) and day(9).

Challenge

  1. Write an arrow function double celsius(double f) => … that converts Fahrenheit to Celsius with (f - 32) * 5 / 9. Use forEach on [212.0, 32.0] to print <f>°F = <c>°C for each value.
  2. Starting with int balance = 500, store var interest = (int b) => 0;. Add interest(balance) to balance and print it. Then swap in a rule giving 10% with ~/ (b ~/ 10), add the interest again, and print the balance.

Practice Answers

Question 1 — Solution

dart
int twice(int n) => n * 2;

void main() {
  print(twice(21));
}
output
42

Question 2 — Solution

dart
bool isPositive(int n) => n > 0;

void main() {
  print(isPositive(-4));
}
output
false

Question 3 — Solution

dart
void welcome(String name) => print('Welcome, $name!');

void main() {
  welcome('Sara');
}
output
Welcome, Sara!

Question 4 — Solution

dart
void main() {
  var area = (int width, int height) => width * height;
  print(area(12, 5));
}
output
60

Question 5 — Solution

dart
String size(int n) => n > 100 ? 'big' : 'small';

void main() {
  print(size(250));
  print(size(8));
}
output
big
small

Question 6 — Solution

dart
void main() {
  ['Ali', 'Sara', 'Zayan'].forEach((name) => print('Hi, $name'));
}
output
Hi, Ali
Hi, Sara
Hi, Zayan

Question 7 — Solution

dart
void main() {
  var message = (String name) => 'Good morning, $name';
  print(message('Ali'));
  message = (String name) => 'Good night, $name';
  print(message('Ali'));
}
output
Good morning, Ali
Good night, Ali

Question 8 — Solution

dart
String day(int n) => switch (n) {
  1 => 'Mon',
  2 => 'Tue',
  _ => '?',
};

void main() {
  print(day(2));
  print(day(9));
}
output
Tue
?

Question 9 — Solution

dart
double celsius(double f) => (f - 32) * 5 / 9;

void main() {
  [212.0, 32.0].forEach((f) => print('$f°F = ${celsius(f)}°C'));
}
output
212.0°F = 100.0°C
32.0°F = 0.0°C

Question 10 — Solution

dart
void main() {
  int balance = 500;
  var interest = (int b) => 0;
  balance += interest(balance);
  print('Balance: $balance');

  interest = (int b) => b ~/ 10;
  balance += interest(balance);
  print('Balance: $balance');
}
output
Balance: 500
Balance: 550

Quick Revision

  1. What does => expr replace in a function body?
  2. Why can't return or if appear after =>?
  3. Which two expression forms let an arrow function make a choice?
  4. What is an anonymous function?
  5. Why does var add = (int a, int b) { ... }; end with };?
  6. What is the difference between sayHi and sayHi()?
  7. When can a variable holding a function receive a different function?
  8. What does forEach do with the function you pass it?

Key Takeaways

  • => expression is a short body meaning { return expression; }.
  • An arrow body is one expression — no return, if, or loops; use ? : or a switch expression for choices, and braces for anything longer.
  • void functions can use arrows too; => then just means "the body is this expression".
  • An anonymous function has parameters and a body but no name.
  • Stored in a variable, it is called with parentheses like any function — and every argument is still type-checked.
  • A variable can be given a new function of the same type; that is the reason to store one.
  • A function written directly as an argument, as with forEach, needs no name at all.
  • Name fixed jobs, type stored functions' parameters, and never put braces after =>.

What's Next?

Lecture 035 — Functions as First-Class Objects. In this lecture a function sat inside a variable and got swapped for another, and Dart described it as String Function(String). That is no trick: in Dart a function is a value, like a number or a string. Next we see what follows from that — writing function types yourself, and passing named functions around like any other value.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.