-
Notifications
You must be signed in to change notification settings - Fork 24
Asynchronous Communication
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.
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.
In the repository, all the BLOC dart files will contain two classes:
- A status class, wrapping the relevant information for that component
- The actual BLOC class, which keeps a reference to the
StreamController
and the last obtained status.
TheStreamController<T>
(in this repository it's aBehaviorSubject
, which is aStreamController
), exposes aStream
and aSink
. If needed, callbacks can be registered to theStream
with thelisten()
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.