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:
SemanticsNodealready has a Key in it!- Could it just be uploaded to the engine?
- Nah, because this key is associated with the
Semanticsitself???
- PRs that added support for
tooltipin 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
Semanticswidget, passesSemanticsPropertiesto it - The Semantics widget creates its render object – RenderSemanticsAnnotations
- The
RenderSemanticsAnnotationsrender 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
RenderObjectto tell them about its semantics, it callsdescribeSemanticsConfigurationand 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 :)
2025-09-07 – Overload
2024-12-30 – Gradle as task runner
in Flutter projects
2024-08-18 – Ditching ngrok for frp
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