Flutter Under the Hood: Binding
This is the part of my article series about how Flutter works under the hood:
- Flutter Under the Hood
- Flutter Under the Hood: Binding (you are here)
- Flutter Under the Hood: Owners
I recommend you read through it first or refresh it in your memory for a better understanding. In it, I have analyzed one of the key aspects of Flutter — the interaction between trees and distribution of responsibilities among them. However, the question remains: how do you ensure its functionality? This is what we will be focusing on in this article.
Overview
If you open the technical overview of Flutter, you will find this scheme. It shows a series of layers in which the creators of Flutter organized the framework.
They call it layer cake which is a very precise description. It consists of larger and smaller layers.
The framework level contains everything we use when writing apps, including all service classes which allow the code to interact with the lower engine layers. Everything inside this level is written in Dart.
The engine level is positioned lower than the framework level. It contains classes and libraries that ensure the functioning of the framework level, including Dart virtual machine, Skia etc.
The platform level contains mechanisms related to a specific launch platform.
Let us take a closer look at the framework level. In the diagram, it is depicted as layers arranged from higher-level to lower-level ones. At the very bottom, we see the Foundation layer. As the name suggests, this layer is something fundamental and basic that is located at the framework level. Here is what stands in the description of this library:
Core Flutter framework primitives.
The features defined in this library are the lowest-level utility classes and functions used by all the other layers of the Flutter framework.
Among other things, this library also contains BindingBase which is the base class for all Bindings.
Binding
First, let us take a look at what Binding stands for and how it is used in Flutter. As evidenced by its name, Binding has to do with some kind of connection. The documentation on BaseBinding states:
Base class for mixins that provide singleton services (also known as «bindings»). To use this class in an `on` clause of a mixin, inherit from it and implement [initInstances()]. The mixin is guaranteed to only be constructed once in the lifetime of the app (more precisely, it will assert if constructed twice in checked mode).
BaseBinding is an abstract base class. Let us list some of the specific implementations of bindings.
ServicesBinding listens for platform messages and directs them to the handler for incoming messages (BinaryMessenger).
PaintingBinding is responsible for binding to the painting library.
RenderBinding binds the render tree to the Flutter engine.
WidgetBinding binds the widget tree to the Flutter engine.
SchedulerBinding is the scheduler for running immediate tasks such as:
- transient callbacks which are triggered by the system’s Window.onBeginFrame callback (for example, Ticker and AnimationController events)
- persistent callbacks, triggered by the system’s Window.onDrawFrame callback (for instance, events for updating the system’s display after transient callbacks have executed)
- post-frame callbacks which are run after persistent callbacks, just before returning from Window.onDrawFrame
- non-rendering tasks to be run between frames.
SemanticsBinding binds the semantics layer and the Flutter engine.
GestureBinding is a binding for the gesture subsystem.
Binding is, essentially, the glue between the engine level and the framework level of Flutter. Each of them is responsible for a specific line of work.
WidgetsFlutterBinding
To better understand how it all works together, let us look at the starting point for any Flutter app, namely calling runApp. The method we call for is located in the binding.dart, and for a good reason. Its description says that it Inflates the given widget and attaches it to the screen. It works like this:
We came across WidgetsFlutterBinding here. It is an implementation of a concrete binding for apps based on the Widgets framework. This is, essentially, the glue that binds the framework to the Flutter engine. WidgetsFlutterBinding consists of multiple bindings that we have already mentioned: GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding. All of them form a layer able to bind our app to the Flutter engine in all directions.
Going back to launching the app. WidgetsFlutterBinding calls the scheduleAttachRootWidget and scheduleWarmUpFrame methods. Let us see what happens there.
ScheduleAttachRootWidget
The scheduleAttachRootWidget method is a scheduled implementation of attachRootWidget. This method belongs to WidgetsBinding. Its description reads that it attaches the given widget to renderViewElement which is a root element of the element tree.
As we can see from the code, this method creates RenderObjectToWidgetAdapter which is the root widget of the widget tree that acts as a bridge between the trees. If we look at its implementation, we will notice that it does not create RenderObject for itself but returns a value from the container field instead, where we pass renderView from RendererBinding to. This renderView is the root element of the render tree.
RenderView get renderView => _pipelineOwner.rootNode;
PipelineOwner is a manager that is responsible for the rendering pipeline.
Let us go back to the attachRootWidget method. The created adapter calls the attachToRenderTree method which is used to create the root of the element tree for the first time. Note that buildOwner is passed to the attachToRenderTree method. This is the manager class for the widgets framework. It tracks the state of widgets as well as which widgets need rebuilding, and has a number of other important tasks related to updating the state of the widget tree. This way we get the three Flutter trees, and each of them is stored and managed using Bindings.
ScheduleWarmUpFrame
The scheduleWarmUpFrame method belongs to SchedulerBinding and is used to schedule a frame to run as soon as possible, rather than waiting for a system “Vsync” signal. Since this method is used while the app is running, the first frame — which is likely to be expensive — gets some extra time to run. ScheduleWarmUpFrame locks events dispatching until the scheduled frame has completed.
As we can see, both mechanisms that have been launched at the start relate to and interact with different Bindings which are closely connected and provide interaction with the system. Let us sum up with this diagram.
Conclusion
Bindings is an essential mechanism for organizing work of a Flutter app. It links together various aspects of the app and connects them to the engine.
At the same time, it allows us to write apps on the highest-level part of the framework, without having to worry about organizing the rest in a coherent manner. I hope that this article helps you navigate through this segment of the Flutter framework. Stay safe!
References
Flutter