What is a Future and how do I use it?

11,506

Solution 1

In case you are familiar with Task<T> or Promise<T> and the async/ await pattern, then you can skip right to the "How to use a Future with the widgets in Flutter" section.

What is a Future and how do I use it?

Well, the documentation says:

An object representing a delayed computation.

That is correct. It's also a little abstract and dry. Normally, a function returns a result. Sequentially. The function is called, runs and returns it's result. Until then, the caller waits. Some functions, especially when they access resources like hardware or network, take a little time to do so. Imagine an avatar picture being loaded from a web server, a user's data being loaded from a database or just the texts of the app in multiple languages being loaded from device memory. That might be slow.

Most applications by default have a single flow of control. When this flow is blocked, for example by waiting for a computation or resource access that takes time, the application just freezes. You may remember this as standard if you are old enough, but in today's world that would be seen as a bug. Even if something takes time, we get a little animation. A spinner, an hourglass, maybe a progress bar. But how can an application run and show an animation and yet still wait for the result? The answer is: asynchronous operations. Operations that still run while your code waits for something. Now how does the compiler know, whether it should actually stop everything and wait for a result or continue with all the background work and wait only in this instance? Well, it cannot figure that out on it's own. We have to tell it.

This is achieved through a pattern known as and . It's not specific to or , it exists under the same name in many other languages. You can find the documentation for Dart here.

Since a method that takes some time cannot return immediately, it will return the promise of delivering a value when it's done.

That is called a Future. So the promise to load a number from the database would return a Future<int> while the promise to return a list of movies from an internet search might return a Future<List<Movie>>. A Future<T> is something that in the future will give you a T.

Lets try a different explanation:

A future represents the result of an asynchronous operation, and can have two states: uncompleted or completed.

Most likely, as you aren't doing this just for fun, you actually need the results of that Future<T> to progress in your application. You need to display the number from the database or the list of movies found. So you want to wait, until the result is there. This is where await comes in:

Future<List<Movie>> result = loadMoviesFromSearch(input);

// right here, you need the result. So you wait for it:
List<Movie> movies = await result;

But wait, haven't we come full circle? Aren't we waiting on the result again? Yes, indeed we are. Programs would be utterly chaotic if they did not have some resemblence of sequential flow. But the point is that using the keyword await we have told the compiler, that at this point, while we want to wait for the result, we do not want our application to just freeze. We want all the other running operations like for example animations to continue.

However, you can only use the awaitkeyword in functions that themselves are marked as async and return a Future<T>. Because when you await something, then the function that is awaiting can no longer return their result immediately. You can only return what you have, if you have to wait for it, you have to return a promise to deliver it later.

Future<Pizza> getPizza() async {
    Future<PizzaBox> delivery = orderPizza();        

    var pizzaBox = await delivery;

    var pizza = pizzaBox.unwrap();
    
    return pizza;   
}

Our getPizza function has to wait for the pizza, so instead of returning Pizza immediately, it has to return the promise that a pizza will be there in the future. Now you can, in turn, await the getPizza function somewhere.

How to use a Future with the widgets in Flutter?

All the widgets in flutter expect real values. Not some promise of a value to come at a later time. When a button needs a text, it cannot use a promise that text will come later. It needs to display the button now, so it needs the text now.

But sometimes, all you have is a Future<T>. That is where FutureBuilder comes in. You can use it when you have a future, to display one thing while you are waiting for it (for example a progress indicator) and another thing when it's done (for example the result).

Let's take a look at our pizza example. You want to order pizza, you want a progress indicator while you wait for it, you want to see the result once it's delivered, and maybe show an error message when there is an error:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

/// ordering a pizza takes 5 seconds and then gives you a pizza salami with extra cheese
Future<String> orderPizza() {
  return Future<String>.delayed(const Duration(seconds: 5), () async => 'Pizza Salami, Extra Cheese');
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: Scaffold(
        body: Center(
          child: PizzaOrder(),
        ),
      ),
    );
  }
}

class PizzaOrder extends StatefulWidget {
  @override
  _PizzaOrderState createState() => _PizzaOrderState();
}

class _PizzaOrderState extends State<PizzaOrder> {
  Future<String>? delivery;

  @override
  Widget build(BuildContext context) {
    return Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          ElevatedButton(
            onPressed: delivery != null ? null : () => setState(() { delivery = orderPizza(); }),
            child: const Text('Order Pizza Now')
          ),
          delivery == null
            ? const Text('No delivery scheduled')
            : FutureBuilder(
              future: delivery,
              builder: (context, snapshot) {
                if(snapshot.hasData) {
                  return Text('Delivery done: ${snapshot.data}');
                } else if(snapshot.hasError) {
                  return Text('Delivery error: ${snapshot.error.toString()}');
                } else {
                  return const CircularProgressIndicator();
                }
              })
        ]);
  }
}

This is how you use a FutureBuilder to display the result of your future once you have it.

Solution 2

Future<T> returning the potential value which will be done by async work

Eg:

Future<int> getValue() async {
    return Future.value(5);
  }

Above code is returning Future.value(5) which is of int type, but while receiving the value from method we can't use type Future<int> i.e

Future<int> value = await getValue();  // Not Allowed
// Error
A value of type 'Future<int>' can't be assigned to a variable of type 'int'

To solve above getValue() should be received under int type

  int value = await getValue(); // right way as it returning the potential value. 

Solution 3

Here's a list of analogies to Dart's Future from other languages:

  • JS: Promise
  • Java: Future
  • Python: Future
  • C#: Task

Just like in other languages Future is a special type of object which allows to use async/await syntax sugar, write asynchronous code in synchronous/linear way. You return Future from an async method rather than accept a callback as a parameter and avoid the callback hell - both Futures and callbacks solve same problems (firing some code at a latter time) but in different way.

Solution 4

I hope this key point will be informative, I show it in two different Async methods:

Note the following method where showLoading(), getAllCarsFromApi() and hideLoading() are inner Async methods.

If I put the await keyword before showLoading(), the Operation waits until it's done then goes to the next line but I intentionally removed the await because I need my Loading dialog be displayed simultaneously with getAllCarsFromApi() is being processed, so it means showLoading() and getAllCarsFromApi() methods are processed on different Threads. Finally hideLoading() hides the loading dialog.

Future<List<Car>> getData() async{
   showLoading();
   final List<Car> cars = await getAllCarsFromApi();
   hideLoading();
   return cars;
}

Now look at this another Async method, here the getCarByIdFromApi() method needs an id which is calculated from the getCarIdFromDatabase(), so there must be an await keyword before the first method to make the Operation wait until id is calculated and passed to the second method. So here two methods are processed one after another and in a single Thread.

Future<Car> getCar() async{
   int id = await getCarIdFromDatabase();
   final Car car = await getCarByIdFromApi(id);
   return car;
}
Share:
11,506
nvoigt
Author by

nvoigt

Updated on December 22, 2022

Comments

  • nvoigt
    nvoigt 11 months

    I get the following error:

    A value of type 'Future<int>' can't be assigned to a variable of type 'int'
    

    It might be another type instead of int, but basically the pattern is

    A value of type 'Future<T>' can't be assigned to a variable of type 'T'
    

    So...

    • What exactly is a Future?
    • How do I get the actual value I want to get?
    • What widget do I use to display my value when all I have is a Future<T>?
  • Christopher Moore
    Christopher Moore over 3 years
    You might consider adding the equivalent .then examples for async/await and make it clear that await is just syntactic sugar for .then.
  • dev-aentgs
    dev-aentgs over 3 years
    @ChristopherMoore check the comment below this .then and await.
  • Christopher Moore
    Christopher Moore over 3 years
    @dev-aentgs That's inaccurate. await is just syntactic sugar for .then. It exists to make async code appear more like sync code so that it is easier to understand.
  • dev-aentgs
    dev-aentgs over 3 years
    @ChristopherMoore It is more than syntactic sugar another reference
  • Christopher Moore
    Christopher Moore over 3 years
    @dev-aentgs nested .then will have the exact same behavior as await. await was created to make clear and more understandable code.
  • Christopher Moore
    Christopher Moore over 3 years
    @dev-aentgs And if you really need a reference, here is one from what is IMO a more reliable source.
  • nvoigt
    nvoigt over 3 years
    I think you are mixing two things: await and .then are not interchangeable. They do not do the same things. However, a program can achieve the same things with a ton of nested .then chains (and at least one Future.wait) that it can achieve much cleaner and clearer with async/await. I'm not sure if I would call it syntactic sugar though. Where does it stop? Isn't everything outside of mov/cmp/jmp syntactic sugar?
  • Christopher Moore
    Christopher Moore over 3 years
    They certainly are not directly interchangeable, but Future.wait would not be necessary if .then was implemented correctly. As to whether it's really "syntactic sugar", I guess that can be your own opinion, but it appears to be a consensus the await is just syntactic sugar in many of the languages that support it.
  • Christopher Moore
    Christopher Moore over 3 years
    @nvoigt Though since you do say "a program can achieve the same things [...] with .then [...] that it can achieve much cleaner and clearer with async/await" isn't that what syntactic sugar is?
  • Christopher Moore
    Christopher Moore over 3 years
    Regardless, I think it might be a good idea to provide this information within the answer and show alternatives with .then.
  • jamesdlin
    jamesdlin over 3 years
    await is syntactic sugar for .then. await is transformed into corresponding .then (and possibly .catchError and .whenCompleted) callbacks. Anything that can be done with await can be done by manually registering appropriate callbacks on the Future. As for where to draw the line: the typical definition of "syntactic sugar" is something that can be removed from the language without removing functionality.
  • Rageh Azzazy
    Rageh Azzazy almost 3 years
    I still can't use int value = await getValue(); inside the widget build method,, it says 'The await expression can only be used in an async function.' because the Build method is not async Beginner question.. but I don't know where to put it
  • Jitesh Mohite
    Jitesh Mohite almost 3 years
    yes, you can not use directly, you should use FutureBuilder or create new method and used setState() to updte data