Skip to content

Asynchronous Communication

Umberto Sonnino edited this page Jul 16, 2018 · 2 revisions

Sinks and Streams are very effective components that can help developers manage the asynchronous nature of certain applications. The Flutter Hot Reload Game uses the TV App as a server, and the Tablet Apps as clients.

This obviously calls for timely updates to the UI, which in turn can be costly, especially as the UI gets more complicated and riddled with custom elements. Flutter tries to be as efficient as possible in updating its widget tree, but using some intelligent mechanisms, minimal updates the UI can be achieved, as well as cleaner and more readable code.

Basic Structure

The game uses a GameProvider widget to wrap the whole app: it is an InheritedWidget, which makes it accessible throughout the widget tree. The static function GameProveider.of(context) exposes the Game object field, and will grant us access to its underlying logic.

The Game class instantiates all the Business LOgic Components (BLOCs). These BLOCs are a vital part of the app, since they maintain the current state of the game (players, commands, etc.) and they manage the asynchronous updates to the UI. There are four of them:

  • Connection BLOC
  • Game Stats BLOC
  • In Game BLOC
  • Scene BLOC

Since the socket communication is also a vital part of the app, it needs to be initiated as soon as possible. In this repository, we used a Delegate Pattern: the Game implements a SocketDelegate interface, and will handle any update to local and remote data through the appropriate implemented function.

BLOCs, Sinks and Streams

In the repository, all the BLOC dart files will contain two classes:

  1. A status class, wrapping the relevant information for that component
  2. The actual BLOC class, which keeps a reference to the StreamController and the last obtained status.
    The StreamController<T> (in this repository it's a BehaviorSubject, which is a StreamController), exposes a Stream and a Sink. If needed, callbacks can be registered to the Stream with the listen() function.

Whenever new data arrives from the server, the Game class will call the setLast() function, and it’ll add the new data to the sink so that any subscriber can be notified of this update. If there's a subscriber on the other end (the Stream), they'll be notified of this update.

An example can be found in the InGame widget (located in terminal_app/lib/game/widgets). In the widget build function, a StreamBuilder is used in the tree. In its constructor, the relevant stream is passed in (accessed through the Game class), and a builder function is also provided.
This function has two parameters: the BuildContext, that lets us access the GameProvider, and the AsyncSnapshot, which is a snapshot of the latest stream data.
This function will be called only when a new AsyncSnapshot arrives: the UI will update only when needed.

Moreover, instead of letting all the parameters trickle down from the SocketDelegate to the various components of the widget tree, this makes for a much cleaner and readable codebase.

Clone this wiki locally