Home Tutorials Training Consulting Books Company Contact us


Get more...

This tutorial gives an introduction into asynchronous processing the the Flutter framework.

1. What is Flutter

1.1. Introduction to Flutter

Flutter is a UI toolkit to build native, fast and beautiful apps for iOS, Android, the web and desktop with a single codebase. It was introduced by Google in 2017 and has had its first 1.0 release on December 4th, 2018.

Flutter is written in C, C++ and Dart. Unlike Javascript, Dart is a statically typed, class-based language which is able to compile Just in Time (JIT) or Ahead of Time (AOT). Flutter supports hot reload of the code during development, which results in an extremely fast stateful reload of your app during development. The SDK utilizes the Skia Graphics Engine to render.

The following image shows the general Flutter architecture.

flutter sdk overview

The top layer provides the building blocks of Flutter and is purely written in Dart. It contains libraries for animation, painting, rendering and widgets.

The Framework runs on top of the engine, which is primarily written in C/C++. The engine contains three core libraries:

  • Skia — An open source graphics engine used by Google Chrome, Android, Chrome Os and may more.

  • Dart runtime

  • Text — Engine used to render and layout text

The bottom layer is the embedder. This layer is platform specific and is where the surface for rendering Flutter is configured.

Flutter is motivated by the fact that the most successful apps, like the Netflix one, looks the same on platforms like iOS and Android. Platform specific behavior like the scroll behavior is still supported by Flutter.

Flutter draws everything itself. Native component like the one used in the Android SDK may handle such updates in the native components.

1.2. Libraries

https://pub.dev/ provides Flutter and Dart libraries. The Flutter tooling uses the pub tooling to read library and build configuration from a pubspec.yaml file and downloads and updates the application with the required libraries.

1.3. Development environment

Flutter can be developed by only using a text editor and the command line. More advanced tooling is provided by VSCode and Android Studio.

We currently recommend Visual Studio Code as the development tool, as it is fast, stable and lightweight.

2. Asynchronous processing in Flutter with futures, async and await

Asynchronous operations allows your program to stay responsive which performing some tasks. Very potential long running operation should be done asynchronously:

  • Fetching data over a network

  • Writing to a database

  • Reading data from a file

Dart provides strong support for asynchronous programming:

  • Futures

  • Streams

  • async / await

2.1. Isolates

Dart uses Isolates to run threads in. Each isolate has its own memory and event loop and isolates can only communicate with each other by sending messages to them. This brings the advantage that garbage collections can be done on an isolate basis.

2.2. Future

To perform asynchronous operations in Dart, you can use the Future class and the async and await keywords. A future represents the result of an asynchronous operation.

Lots of standard Dart libraries calls, return a Future as return type, for example:

  • http.get

  • SharedPreference.getInstance()

A future can have two states:

  • Uncompleted - When you call a function that returns a future, the function queues up the operation and returns an uncompleted future

  • Completed - When a future’s operation finishes, the future completes with a value or with an error

A Future<T> instance produces a value of type T. If a future doesn’t produce a usable value, then the future’s type is Future<void>. If the function doesn’t explicitly return a value, then the return type also Future<void>

The following is an example for the usage of futures. The "Another output" is written first to the console and after the delay "Testing futures".

Future<void> printSomeThingDelayed() {
  return Future.delayed(Duration(seconds: 3), () => print('Testing futures'));
}

main() {
  printSomeThingDelayed();
  print('Another output ...');
}

Future provides a few helper methods in case the value or error is already present:

  • Future.value

  • Future.exception

For mocking slow data, you can use the delayed method.

* Future.delayed(Duration(seconds:5), () => 12,);

Future allows to register a callback via the then method which is called then the Future is completed with a value. Then also returns a Future so future calls can be chained. The catchError method allows to register a hook for the error case. The whenComplete method is used to executed code after the complete or error as been received an processed.

Here is a complete example:

void main() {
  var userIdFuture = getUserId();
  // register callback which is executed once the future is completed
  userIdFuture.then((userId) => print(userId));

  // Hello wird vor 77 ausgegeben, 77 wird erst nach 3 Sekunden ausgegeben
  print('Hello');
}

// method which computes a future
Future<int> getUserId() {
  // simulate a long network call
  return Future.delayed(Duration(seconds: 3), () => 77);
}

2.3. Await and async

The async keyword is used to define a function as asynchronous The await keywords allows to wait for the result of an asynchronous function and works only in async functions.

For example, you can declare your main method as async and await a asynchronous result.

Future<void> printSomeThingDelayed() {
  return Future.delayed(Duration(seconds: 3), () => print('Testing futures'));
}

main() async {
  await printSomeThingDelayed(); // blocks until the result returns
  print('Another output ...');
}

Here is a complete example:

void main() {
  var userIdFuture = getUserId();
  // register callback which is executed once the future is completed
  userIdFuture.then((userId) => print(userId));

  // hello is written first to the console and after 3 seconds 77
  print('Hello');
}

// method which computes a future
Future<int> getUserId() {
  // simulate a long network call
  return Future.delayed(Duration(seconds: 3), () => 77);
}

Future<String> getUserName() async {
  var userId = await getUserId();
  return Future.delayed(Duration(seconds: 1), () => "Username $userId");
}

To handle errors in async functions you use try-catch.

2.4. FutureBuilder

The FutureBuilder widget allows to use a future directly and to build its UI based on the state of the widget.

FutureBuilder<String>(
  future: _calculation, // a previously-obtained Future<String> or null
  builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
    switch (snapshot.connectionState) {
      case ConnectionState.none:
        return Text('Press button to start.');
      case ConnectionState.active:
      case ConnectionState.waiting:
        return Text('Awaiting result...');
      case ConnectionState.done:
        if (snapshot.hasError)
          return Text('Error: ${snapshot.error}');
        return Text('Result: ${snapshot.data}');
    }
    return null; // unreachable
  },
)

2.5. Streams

Streams support reactive programming. They work similar to a Future but instead of returning a single value, they return a series of events. Streams are designed for single subscriptions. You can use the asBroadcastStream method to convert this stream into a stream which supports multiple subscriptions.

You can subscribe to a Stream via the following methods:

  • listen

  • onError

  • onDone - called once the stream is closed

Streams can be manipulated via methods:

  • map - converts each element of the stream into something else

  • where - allows to filter elements of the stream

  • distinct - filters out consecutive identical values

Streams can be created via the StreamController class.

2.6. StreamBuilder in Flutter

StreamBuilder is a Flutter widgets which allows to build your UI based on the state of the stream.

3. Links and Literature

Legal Privacy Policy Change consent