Dart Lesson 31 – Futures | Dataplexa

Futures in Dart

In this lesson, you will learn about Futures in Dart. A Future represents a value that will be available at some point in the future.

Futures are the backbone of asynchronous programming in Dart and are heavily used in file handling, APIs, databases, and network operations.


What Is a Future?

A Future is an object that represents the result of an asynchronous operation.

That result can be:

  • A value (successful completion)
  • An error (failed operation)

Why Futures Are Needed

Operations like fetching data from an API or reading files take time. Instead of stopping the program, Dart uses futures to continue execution.

This keeps applications responsive and efficient.


Basic Future Example

Let’s start with a simple future that returns a value after a delay.

Future getMessage() {
  return Future.delayed(
    Duration(seconds: 2),
    () => "Welcome to Dart Futures"
  );
}

Consuming a Future Using then()

void main() {
  print("Start");

  getMessage().then((message) {
    print(message);
  });

  print("End");
}

Output order:

  • Start
  • End
  • Welcome to Dart Futures

Future States

Every future has three states:

  • Uncompleted – operation is still running
  • Completed with data – operation finished successfully
  • Completed with error – operation failed

Future Returning Numeric Data

Let’s simulate a calculation that takes time.

Future calculateTotal() {
  return Future.delayed(
    Duration(seconds: 3),
    () => 150 + 250 + 100
  );
}

Handling Numeric Result

void main() {
  calculateTotal().then((total) {
    print("Total Amount: $total");
  });
}

Future with Error

Futures can also fail due to errors.

Future divide(int a, int b) {
  return Future(() {
    if (b == 0) {
      throw Exception("Cannot divide by zero");
    }
    return a ~/ b;
  });
}

Handling Errors Using catchError()

void main() {
  divide(20, 0)
      .then((result) => print(result))
      .catchError((error) => print(error));
}

Using whenComplete()

whenComplete() runs code regardless of success or failure.

divide(10, 2)
    .then((value) => print("Result: $value"))
    .catchError((e) => print(e))
    .whenComplete(() => print("Operation completed"));

Chaining Multiple Futures

You can chain futures where the output of one feeds into another.

Future fetchOrderId() {
  return Future.delayed(Duration(seconds: 1), () => 101);
}

Future fetchOrderStatus(int id) {
  return Future.delayed(
    Duration(seconds: 2),
    () => "Order $id shipped"
  );
}

Chained Execution

fetchOrderId()
    .then((id) => fetchOrderStatus(id))
    .then((status) => print(status));

Future.wait()

Future.wait() runs multiple futures in parallel.

Future.wait([
  Future.delayed(Duration(seconds: 2), () => "User Loaded"),
  Future.delayed(Duration(seconds: 3), () => "Orders Loaded"),
  Future.delayed(Duration(seconds: 1), () => "Settings Loaded"),
]).then((results) {
  results.forEach(print);
});

Real-World Example: Dashboard Load

This pattern is commonly used when loading dashboards with multiple data sources.


Common Mistakes with Futures

  • Forgetting to handle errors
  • Blocking logic inside futures
  • Nesting futures unnecessarily

📝 Practice Exercises


Exercise 1

Create a future that returns a username after 2 seconds.

Exercise 2

Chain two futures together.

Exercise 3

Handle an error using catchError.


✅ Practice Answers


Future getUsername() {
  return Future.delayed(Duration(seconds: 2), () => "dataplexa_user");
}

void main() {
  getUsername()
      .then((user) => print("User: $user"))
      .catchError((e) => print(e))
      .whenComplete(() => print("Done"));
}

What’s Next?

In the next lesson, you will learn about Async and Await, which makes working with futures simpler and more readable.