Semantics in Flutter - under the hood
Thursday, 30 November 2023
This post is WIP. I might or might not come back to this rabbit hole and finish it.
Findings:
SemanticsNode
already has a Key in it!- Could it just be uploaded to the engine?
- Nah, because this key is associated with the
Semantics
itself???
- PRs that added support for
tooltip
in semantics. Something similar could be done withidentifier
.- framework part https://github.com/flutter/flutter/pull/87684
- engine part
- original https://github.com/flutter/engine/pull/27893
- relanded https://github.com/flutter/engine/pull/28211
- PR that optimized semantics. Has some stats about how many “semantics” there are in big apps.
How does semantics flow from the Framework to the Engine?
In the framework
- Developer creates a
Semantics
widget, passesSemanticsProperties
to it - The Semantics widget creates its render object – RenderSemanticsAnnotations
- The
RenderSemanticsAnnotations
render object callsmarkNeedsSemanticsUpdate()
. This method is inherited from the RenderObject abstract class markNeedsSemanticsUpdate()
adds the render object toPipelineOwner._nodesNeedingSemantics
.
Also, every RenderObject
manages its own
SemanticConfiguration
.
- when somebody wants the
RenderObject
to tell them about its semantics, it callsdescribeSemanticsConfiguration
and passes (TODO)
At some later point, a new frame is scheduled by the engine.
This is handled by RendererBinding
in the persistent
frame callback that it
registered. This callback calls RendererBinding
’s
drawFrame()
,
which drives the frame rendering pipeline. In one of the last
stages of this pipeline, it callsPipelineOwner.flushSemantics()
.
This gathers _nodesNeedingSemantics
.
Finally, SemanticsOwner.sendSemanticsUpdate()
is
called. This function does (quite some stuff), but most
importantly, it finally calls
SemanticsUpdateBuilder.build()
to obtain a SemanticsUpdate
.
💡 SemanticsUpdateBuilder is an abstract class from the engine. It’s implemented in C++ (header) (source).
Then, the SemanticsOwner.onSemanticsUpdate
callback is called and the SemanticsUpdate
is passed
to it as an argument.
(omitted some calls)
Then that SemanticsUpdate
is passed to the engine
by calling
RenderView.updateSemantics()
.
In the engine
TODO, but generally:
- first, the SemanticsUpdate lands in Engine’s C++ code, and then
- it is delivered to the appropriate embedder APIs
- on Android, JNI is heavily used
Naming observations
- Methods that start with “handle”,
e.g.
handlePlatformBrightnessChanged
, are callbacks to be run when the engine says so
What happens when accessibility is enabled?
“how Flutter learns that it has to start sending accessibility info”
iOS
PlatformConfiguration.cc | UpdateSemanticsEnabled (crossing C++/Dart boundary)
This callbacks gets invoked: PlatformDispatcher.onSemanticsEnabledChanged
That callback is set up by
SemanticsBinding
: link
The end :)
2024-11-12 – Liminal
spaces
2024-08-18 – frp (Fast
Reverse Proxy) instead of ngrok
2024-08-16 – Cirrus CI is the best CI system
out there
2024-08-14 – Going to Berlin for
Droidcon/Fluttercon
2024-06-25 – I was awarded Google Open Source Peer
Bonus
2024-06-04 – My journey to Google I/O
’24
2024-05-11 – GitHub Actions
beg for a supply chain attack
2024-03-19 – Writing a custom Dart
VM service extension (part 1)
2024-02-08 – On using smartphone for things
that make sense
2023-11-30 – Semantics in Flutter - under
the hood
2023-11-25 – Flutter Engine notes
2023-09-17 – Creating
and managing Android Virtual Devices using the terminal
2023-05-27 – Suckless
Android SDK setup
2023-05-26 – Let’s start over
2023-05-21 –
Short thought on “The Zen of
Unix”
2023-05-15 – Notes about “flutter
assemble”
2019-01-07 – Google Code-in
2018