On this page23 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What Is a Future?
- 2. Printing a Future
- 3. The Three States of a Future
- 4. Getting the Value with .then
- 5. The Program Does Not Wait
- 6. Future.value — Already Completed
- 7. Future(() => ...) — Run Work Later
- 8. Chaining .then Calls
- 9. Returning a Future from a Function
- 10. Future<void> — Work with No Result
- 11. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 88 — Futures
| Category | 15 — Asynchronous Programming |
| Lecture | 088 of 140 |
| Estimated video length | 10 min |
| Target | Dart 3.12.2 |
| Level | Intermediate |
A slow task cannot hand back its result straight away — but making the program wait would freeze it. A
Futurehands back a promise of the result instead, and lets you say what should happen when it arrives.
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.
Prerequisites
Students should already understand, from Lectures 030–087:
- functions, return types, and arrow and anonymous functions (Lectures 030–036)
- generic types such as
List<int>(Lectures 072–073) - exceptions (Lectures 083–086)
- blocking code, the event loop, and a first look at
Future.delayed(Lecture 087)
Connection With Previous Lectures
In Lecture 087 you saw that a slow task — downloading data, reading a file — must not block the program. Dart starts the task, keeps running the rest of the code, and comes back when the task is done.
That raises a question. A normal function hands back its result with return. If the result
is not ready yet, what does the function hand back? And how does the rest of the program
get the value once it finally exists? The answer to both is a Future.
Learning Objectives
By the end of this lecture you will be able to:
- Explain what a
Future<T>represents - Name the three states a future can be in
- Create futures with
Future.delayed,Future.value, andFuture(() => ...) - Use
.thento run code when a future's value arrives - Predict the order of output when futures are involved
- Chain several
.thencalls - Write functions that return
Future<T>andFuture<void> - Recognize the errors Dart gives when a future is used as a plain value
1. What Is a Future?
A Future<T> is an object that promises a value of type T later. A
Future<int> will one day hold an int; a Future<String> will hold a String.
Analogy — at a busy café you order a coffee and get a receipt with a number. The receipt is not the coffee, but it promises one. You sit down and do other things, and when your number is called, you collect the real coffee. A
Futureis that receipt.
2. Printing a Future
Throughout this lecture we simulate a slow network call with Future.delayed. It waits for a
Duration, then runs a function and uses its result as the value:
void main() {
var marks = Future.delayed(Duration(seconds: 2), () => 85);
print(marks);
}Instance of 'Future<int>'We did not get 85. marks is the receipt, not the coffee. Dart infers its type as
Future<int> from the function's result, and printing it describes the object.
3. The Three States of a Future
A future is always in one of three states:
| State | Meaning | Café |
|---|---|---|
| Uncompleted | the work is still running | coffee is being made |
| Completed with a value | the result is ready | your coffee is served |
| Completed with an error | the work failed | "sorry, we are out of milk" |
In section 2, marks was uncompleted when we printed it; two seconds later it completed with
the value 85. A future completes once and then never changes. Handling the error case
is the topic of Lecture 090 — Future Error Handling.
4. Getting the Value with .then
To use the value, give the future a function with .then. Dart calls that function with the
value as soon as the future completes:
void main() {
var marks = Future.delayed(Duration(seconds: 2), () => 85);
marks.then((value) {
print('Marks arrived: $value');
});
}Marks arrived: 85The text appears after a two-second pause. .then is like telling the café, "call my number
when it's ready."
5. The Program Does Not Wait
.then only registers the function. The next line runs immediately:
void main() {
print('1. Asking for marks');
Future.delayed(Duration(seconds: 2), () => 85).then((value) {
print('3. Marks: $value');
});
print('2. Doing other work');
}1. Asking for marks
2. Doing other work
3. Marks: 85Line 2 is printed before line 3, even though it comes later in the code. This is the
non-blocking behavior from Lecture 087, now with a value at the end of it.
6. Future.value — Already Completed
Future.value creates a future that already has its value. It is handy when a function must
return a future but the answer is known:
void main() {
var name = Future.value('Sara');
print('Before');
name.then((value) => print('Name: $value'));
print('After');
}Before
After
Name: SaraEven with the value ready, the .then function still runs after the current code
finishes. A .then callback never interrupts the line-by-line code that is running.
7. Future(() => ...) — Run Work Later
The Future constructor takes a function, runs it soon (but not right now), and completes
with its result:
void main() {
var total = Future(() => 500 * 3);
total.then((value) => print('Total: $value'));
print('Calculating...');
}Calculating...
Total: 1500| Constructor | Completes |
|---|---|
Future.delayed(duration, () => v) | after the duration |
Future.value(v) | already holds v |
Future(() => v) | as soon as the current code finishes |
8. Chaining .then Calls
.then itself returns a new future that completes with whatever your function returns. So
you can attach another .then, each step receiving the previous step's result:
void main() {
Future.delayed(Duration(seconds: 1), () => 80)
.then((marks) => marks + 5)
.then((withBonus) => 'Final marks: $withBonus')
.then((message) => print(message));
}Final marks: 8580 becomes 85, 85 becomes a String, and the last step prints it.
9. Returning a Future from a Function
A function that does slow work returns a Future<T>. Its return type says "you will get a
T, later":
Future<int> fetchBalance() {
return Future.delayed(Duration(seconds: 2), () => 1000);
}
void main() {
print('Loading balance...');
fetchBalance().then((balance) => print('Balance: $balance'));
}Loading balance...
Balance: 1000The function itself returns at once — with the receipt. The caller uses .then to collect
the 1000.
10. Future<void> — Work with No Result
Some slow work produces nothing useful, such as saving a file. Its type is Future<void>:
the future only tells you when the work is finished.
Future<void> saveScore(int score) {
return Future.delayed(Duration(seconds: 1), () {
print('Saved score $score');
});
}
void main() {
saveScore(42).then((_) => print('All done'));
}Saved score 42
All doneThere is no value to receive, so the .then parameter is named _.
11. A Real-World Example
An app loads a student's name, then that student's marks — two simulated network calls, one after the other:
Future<String> fetchStudent() {
return Future.delayed(Duration(seconds: 1), () => 'Ali');
}
Future<List<int>> fetchMarks(String name) {
return Future.delayed(Duration(seconds: 1), () => [80, 75, 85]);
}
void main() {
print('Loading report...');
fetchStudent()
.then((name) {
print('Student: $name');
return fetchMarks(name);
})
.then((marks) {
var total = 0;
for (var mark in marks) {
total += mark;
}
print('Marks: $marks');
print('Total: $total');
});
print('Screen stays responsive');
}Loading report...
Screen stays responsive
Student: Ali
Marks: [80, 75, 85]
Total: 240The first .then returns another future. Dart waits for it and passes its value — the
list — to the next .then. Meanwhile main has already printed its last line.
Chains of .then get hard to read quickly. In Lecture 089 you will write the same program
with async and await, top to bottom like normal code.
Code Examples
Example 1 — Printing a Future
void main() {
var city = Future.value('Karachi');
print(city);
}Instance of 'Future<String>'Example 2 — Reading the Value
void main() {
var city = Future.value('Karachi');
city.then((value) => print(value));
}KarachiExample 3 — A Delayed Value
void main() {
Future.delayed(
Duration(milliseconds: 500),
() => 'Ready',
).then((value) => print(value));
print('Waiting...');
}Waiting...
ReadyExample 4 — The Future Constructor
void main() {
Future(() => 12 * 12).then((value) => print('Square: $value'));
print('First line');
}First line
Square: 144Example 5 — A Chain of Three Steps
void main() {
Future.value(10).then((n) => n * 2).then((n) => n + 1).then((n) => print(n));
}21Example 6 — A Future<void> Function
Future<void> greet(String name) {
return Future.delayed(Duration(seconds: 1), () => print('Hello, $name'));
}
void main() {
greet('Zayan').then((_) => print('Greeting finished'));
}Hello, Zayan
Greeting finishedExample 7 — Student Marks
Future<int> fetchMarks(String student) {
return Future.delayed(Duration(seconds: 1), () => 78);
}
void main() {
fetchMarks('Sara').then((marks) {
var result = marks >= 50 ? 'Pass' : 'Fail';
print('Sara: $marks ($result)');
});
}Sara: 78 (Pass)How it works
fetchMarksreturns aFuture<int>that completes with78after one second.- The
.thenfunction receives78and decides the result.
Example 8 — Shopping Total
Future<int> fetchPrice(String product) {
return Future.delayed(Duration(seconds: 1), () => 500);
}
void main() {
var quantity = 3;
fetchPrice('Notebook').then((price) {
var total = price * quantity;
print('Total: $total');
});
}Total: 1500How it works
- The price arrives after one second.
- The
.thenfunction multiplies it byquantityand prints the total.
Example 9 — Bank Balance
Future<double> fetchBalance() {
return Future.delayed(Duration(seconds: 2), () => 1000.0);
}
void main() {
print('Checking balance...');
fetchBalance().then((balance) {
var afterDeposit = balance + 250.5;
print('Balance: $afterDeposit');
});
print('You can keep using the app.');
}Checking balance...
You can keep using the app.
Balance: 1250.5How it works
mainprints its two lines straight away.- Two seconds later the balance arrives and the deposit is added.
Example 10 — Temperature from a Weather Service
Future<double> fetchCelsius(String city) {
return Future.delayed(Duration(seconds: 1), () => 25.0);
}
void main() {
fetchCelsius('Lahore')
.then((celsius) => celsius * 9 / 5 + 32)
.then((fahrenheit) => print('Lahore: $fahrenheit °F'));
}Lahore: 77.0 °FHow it works
- The "weather service" answers with
25.0after one second. - The chain converts it to Fahrenheit, then prints it.
Common Beginner Mistakes
1. Expecting the value instead of the future
Future<int> fetchMarks() {
return Future.delayed(Duration(seconds: 2), () => 85);
}
void main() {
// int marks = fetchMarks(); // ✗
}Error: A value of type 'Future<int>' can't be assigned to a variable of type 'int'.A future is not its value. Use fetchMarks().then((marks) => ...).
2. Forgetting Future in the return type
// int fetchMarks() {
// return Future.delayed(Duration(seconds: 1), () => 85); // ✗
// }Error: A value of type 'Future<int>' can't be returned from a function with return type 'int'.A function that returns a future must say so: Future<int> fetchMarks().
3. Expecting code after .then to wait
void main() {
var total = 0;
Future.value(500).then((price) => total = price);
print(total); // prints 0, not 500
}The print runs before the .then function. Code that needs the value belongs inside
the .then.
4. Mismatching the future's type
void main() {
// Future<String> name = Future.delayed(Duration(seconds: 1), () => 85); // ✗
}Error: A value of type 'int' can't be returned from a function with return type 'FutureOr<String>'.A Future<String> must complete with a String. FutureOr<String> means "a String or a
future of one."
Best Practices
- Give functions that do slow work an explicit
Future<T>return type. - Use
Future<void>when the caller only needs to know the work is finished. - Put code that depends on a future's value inside
.then, not after it. - Use
Future.delayedto simulate slow work while learning and testing. - Keep
.thenchains short — Lecture 089 offers a cleaner style.
Mini Challenge
Write Future<int> fetchStock(String item) that "returns" 12 after one second. In main,
print Checking stock..., then print the stock when it arrives.
Checking stock...
Pens in stock: 12Try it yourself before opening the solution.
Show solution
Future<int> fetchStock(String item) {
return Future.delayed(Duration(seconds: 1), () => 12);
}
void main() {
print('Checking stock...');
fetchStock('Pens').then((stock) => print('Pens in stock: $stock'));
}Checking stock...
Pens in stock: 12Practice Questions
Work through these in order — they run from beginner to application to challenge.
Beginner
- Create
Future.value(7)and print the future itself. What does the output say? - Create
Future.value('Ali')and use.thento printHello, Ali. - Use
Future.delayedto produce'Done'after one second, and print it with.then.
Application
-
Predict the output, then run it:
void main() { print('A'); Future.value('B').then((v) => print(v)); print('C'); } -
Write
Future<int> fetchScore()that completes with40after one second. Inmain, printScore: 40. -
Starting from
Future.value(100), chain.thencalls that subtract30, then double the result, then print it. -
Write
Future<void> sendMessage(String text)that printsSent: <text>after one second. Call it with'Hi Sara'and printFinishedwhen it completes.
Challenge
-
Write
Future<double> fetchPrice()that completes with1200.0after one second. Apply a 10% discount in one.thenand printFinal price: 1080.0in the next. -
Write
fetchUser()('Zayan'after one second) andfetchAge(String name)(6after one second). Chain them to printZayan is 6. -
This program prints
Total: 0. Fix it so it printsTotal: 900:void main() { var total = 0; Future.value(450).then((price) => total = price * 2); print('Total: $total'); }
Practice Answers
Question 1 — Solution
void main() {
print(Future.value(7));
}Instance of 'Future<int>'It describes the future, not the value 7.
Question 2 — Solution
void main() {
Future.value('Ali').then((name) => print('Hello, $name'));
}Hello, AliQuestion 3 — Solution
void main() {
Future.delayed(
Duration(seconds: 1),
() => 'Done',
).then((value) => print(value));
}DoneQuestion 4 — Solution
A
C
BThe .then function runs only after main's own lines have finished.
Question 5 — Solution
Future<int> fetchScore() {
return Future.delayed(Duration(seconds: 1), () => 40);
}
void main() {
fetchScore().then((score) => print('Score: $score'));
}Score: 40Question 6 — Solution
void main() {
var start = Future.value(100);
start.then((n) => n - 30).then((n) => n * 2).then((n) => print(n));
}140Question 7 — Solution
Future<void> sendMessage(String text) {
return Future.delayed(Duration(seconds: 1), () => print('Sent: $text'));
}
void main() {
sendMessage('Hi Sara').then((_) => print('Finished'));
}Sent: Hi Sara
FinishedQuestion 8 — Solution
Future<double> fetchPrice() {
return Future.delayed(Duration(seconds: 1), () => 1200.0);
}
void main() {
fetchPrice()
.then((price) => price - price * 0.1)
.then((finalPrice) => print('Final price: $finalPrice'));
}Final price: 1080.0Question 9 — Solution
Future<String> fetchUser() {
return Future.delayed(Duration(seconds: 1), () => 'Zayan');
}
Future<int> fetchAge(String name) {
return Future.delayed(Duration(seconds: 1), () => 6);
}
void main() {
fetchUser().then((name) {
return fetchAge(name).then((age) => print('$name is $age'));
});
}Zayan is 6The inner .then is nested so it can still use name.
Question 10 — Solution
Move the print inside the .then, where the value exists:
void main() {
Future.value(450).then((price) {
var total = price * 2;
print('Total: $total');
});
}Total: 900Quick Revision
- What does a
Future<String>promise? - What are the three states of a future?
- What does
printshow when you print aFuture<int>? - Which constructor creates a future that already has its value?
- Does the line after a
.thencall wait for the future? - What does
.thenitself return? - When should a function return
Future<void>?
Key Takeaways
- A
Future<T>is a promise of aTthat arrives later. - A future is uncompleted, completed with a value, or completed with an error — and completes only once.
- Printing a future shows
Instance of 'Future<int>', not the value. - Create futures with
Future.delayed,Future.value, andFuture(() => ...). .thenregisters code to run when the value arrives; the program does not wait..thenreturns a new future, so calls can be chained.- Slow functions return
Future<T>, orFuture<void>when there is no result.
What's Next?
Lecture 089 — async & await. .then works, but every step adds another callback, and
the code no longer reads top to bottom. Next, async and await let you wait for a future's
value in a straight line — var marks = await fetchMarks(); — without blocking the program.