Dart/Flutter Future tutorial with examples

Asynchronous programming allows a program to do work while waiting for something else to complete. In this tutorial, we’ll show you how to work with Future in Dart (also in Flutter). At the end, you’re gonna know:

  • Introduction to Dart Future
  • How to create Future, Future delayed and value methods
  • Basic Future usage: then, catchError and callback
  • Way to use Future async-await
  • Future multiple await/then for chain of multiple asynchronous methods
  • Equivalent of ‘finally’: Future whenComplete
  • Future wait for multiple futures complete

Related Posts:
Dart/Flutter Constructors tutorial with examples
Dart/Flutter String Methods & Operators tutorial with examples
Dart/Flutter List Tutorial with Examples
Dart/Flutter Map Tutorial with Examples


Dart/Flutter Future overview

A Future represents a potential value (success), or error (fail), that will be available in the future. Generally we will need to work with a future value in following cases:

  • getting data from a server
  • querying data from a database
  • reading a file
  • doing a complicated computational work
Future<int> future = getFuture();
Future<Tutorial> tutorialsFut = getTutorialsFromDatabase(query);
Future<Data> dataFut = fetchDataFromServer(url);
Future<int> resultFut = computeBigData(value);

Dart/Flutter Future then

We can use then() to indicate code that runs when the future completes.

For example, fetchDataFromServer() returns a Future.

Future<Data> future = fetchDataFromServer(url);
future.then((Data data) {
  print(data.values);
});

Dart/Flutter Future callback

In the example above, we register a callback that handles the value of a Future result. How about an error? We’re gonna use catchError(callback).

For example:

Future<Data> future = fetchDataFromServer(url);
future.then((data) => handleData(data))
      .catchError((error) => handleError(error));

Dart/Flutter async-await

The async and await keywords provide a declarative way to define asynchronous function (that returns a Future) and use its result.

We must remember 2 basic conditions:

  • Asynchronous function has async before the function body.
  • The await keyword works only in async functions.

For example, we’re gonna re-write the Future then above with async-await style.

main() async {
  Data data = await fetchDataFromServer(url);
  handleData(data);
}

Furthermore, then-catchError is equivalent to async-await like this:

main() async {
  try {
    Data data = await fetchDataFromServer(url);
    handleData(data);
  } catch (error) {
    handleError(error);
  }
}

Let’s continue with some simple ways to create a Future.

Dart/Flutter Future delayed

Future<T>.delayed(Duration duration, [computation()?])

delayed() method creates a Future that runs computation after the given duration has passed, and the future is completed with the result of the computation.

If the duration is 0 or less, it completes no sooner than in the next event-loop iteration, after all microtasks have run.

For example:

Future.delayed(Duration(seconds: 2), () => 'Delayed 2 seconds.')
      .then((result) => print(result));

async-await:

var result = await Future.delayed(Duration(seconds: 2), () => 'Delayed 2 seconds.');
print(result);

Output:

(after 2 seconds)
Delayed 2 seconds.

Dart/Flutter Future value

Future<T>.value(value)

The method Future.value() creates a Future completed with value.

For example:

Future.value("bezkoder.com")
      .then((result) => print(result));

async-await:

var result = await Future.value("bezkoder.com");
print(result);

Output:

bezkoder.com

Dart/Flutter Future error

Future.error(Object error) creates a future that completes with an error.

For example:

Future.error('> This is an error message!')
      .catchError((err) => print(err));

async-await:

try {
  await Future.error('> This is an error message!');
} catch (err) {
  print(err);
}

Output:

> This is an error message!

Write asynchronous Function with Future

Now we create a asynchronous function that returns a Future. The function receives an input int number. If the input number is less than 0, it completes with a Future error, or else, completes after given time and return the input number.

Future<int> doInSeconds(int sec) {
  print('> Processing with value=${sec}...');

  if (sec <= 0) {
    return Future.error('> input \'sec\' must be greater than 0');
  }

  return Future.delayed(Duration(seconds: sec), () {
    print('> Something done in ${sec} second(s).');
    return sec;
  });
}

Let’s use doInSeconds() function.

doInSeconds(2).then((result) {
  print(result);
}).catchError((error) {
  print('> inside catchError');
  print(error);
});

Or:

try {
  var result = await doInSeconds(2);
  print(result);
} catch (error) {
  print(error);
}

Output:

> Processing with value=2...
(after 2 seconds)
> Something done in 2 second(s).
2

Check the catching Error:

try {
  var result = await doInSeconds(-1);
  print(result);
} catch (error) {
  print('> inside catchError');
  print(error);
}

Output:

> Processing with value=-1...
> inside catchError
> input 'sec' must be greater than 0

Chain of multiple asynchronous methods in Dart/Flutter

This is how we handle the case in that we need to call run multiple asynchronous functions in a certain order.

Future future = doInSeconds(1);
future
    .then((v1) => doInSeconds(v1 + 1))
    .then((v2) => doInSeconds(v2 + 1))
    .catchError((error) => print(error));

Output:

> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=2...
> Something done in 2 second(s).
> Processing with value=3...
> Something done in 3 second(s).
  • If the callback inside then() returns a Future, then() returns a Future that will complete with the same result.
  • If the callback returns a value of any other type, then() creates a new Future that completes with the value.

Dart/Flutter multiple await

Let’s run multiple asynchronous functions in chain using async-await:

try {
  final v1 = await doInSeconds(1);
  final v2 = await doInSeconds(v1 + 1);
  await doInSeconds(v2 + 1);
} catch (error) {
  print(error);
}

Output:

> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=2...
> Something done in 2 second(s).
> Processing with value=3...
> Something done in 3 second(s).

Dart/Flutter Future whenComplete

We’ve known that then-catchError is equivalent to a try-catch, so whenComplete is for finally.

The callback inside whenComplete() is called after all, whether the result is with a value or with an error.

try-catch-finally:

try {
  final v1 = await doInSeconds(1);
  final v2 = await doInSeconds(v1 + 1);
  await doInSeconds(v2 + 1);
} catch (error) {
  print(error);
} finally {
  print("Done!");
}

equivalent:

Future future = doInSeconds(1);
future
    .then((v1) => doInSeconds(v1 + 1))
    .then((v2) => doInSeconds(v2 + 1))
    .catchError((error) => print(error))
    .whenComplete(() => print("Done!"));

Output:

> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=2...
> Something done in 2 second(s).
> Processing with value=3...
> Something done in 3 second(s).
Done!

Check another code that will cause an error.

Future future = doInSeconds(1);
future
    .then((v1) => doInSeconds(v1 - 1))
    .then((v2) => doInSeconds(v2 - 1))
    .catchError((error) => print(error))
    .whenComplete(() => print("Done!"));

Output:

> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=0...
> input 'sec' must be greater than 0
Done!

Future wait in Dart/Flutter

If you need to run many asynchronous functions and wait for them all to complete before continuing something else, you can use static method: Future.wait() to manage multiple Futures:

var query = doInSeconds;
var compute = doInSeconds;
var work = doInSeconds;
await Future.wait([
  query(1),
  compute(6),
  work(3),
]);
print('Done!');

Output:

> Processing with value=1...
> Processing with value=6...
> Processing with value=3...
> Something done in 1 second(s).
> Something done in 3 second(s).
> Something done in 6 second(s).
Done!

Conclusion

In this tutorial, we’ve learned overview of a Dart/Flutter Future, how to create a Future simply using delayed or value method, how to work with then-catchError-whenComplete or try-catch-finally with Future async-await, way to handle chain of multiple asynchronous methods or Future wait for multiple futures complete.

Happy learning! See you again!

Further Reading

Dart/Flutter Constructors tutorial with examples
Dart/Flutter String Methods & Operators tutorial with examples
Dart/Flutter List Tutorial with Examples
Dart/Flutter Map Tutorial with Examples

Leave a Reply

Your email address will not be published. Required fields are marked *