-
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. By connecting various different components, the Flutter Hot Reload Game relied on the TV App to act as the server, and the Tablet Apps to receive/send information from/to it in order to maintain a coherent state of the game.
This obviously calls for some timely updates to the UI, which can be costly, especially if the UI is complicated. Flutter tries to be as efficient as possible in updating its components, and if by 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.