inset-embedder 0.19.0

dart:ui host traits and value types for Inset
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# inset-embedder/src
No Flutter crate in this shape. Closest analogue: dart:ui `PlatformDispatcher` / `hooks.dart` / `FlutterView`.

## Identical

- key.rs → dart:ui key.dart
- text.rs → dart:ui text.dart (the text and font enums, `FontFeature`, `FontVariation`)
- locale.rs → dart:ui `Locale`
- `AppLifecycleState`, `AppExitResponse`, `AppExitType` in platform.rs → dart:ui `platform_dispatcher.dart`

## painting.rs → dart:ui `Canvas` / `Paint` / `Paragraph` / `TextStyle`

- Change: `ImageFilter` is an enum over what this host can replay — a blur, a colour filter and a composition of two.
  Reason: platform — valo's display list has no other image filter.
  Affect: the tile-mode blur and the dilate, erode, matrix and shader filters cannot be built at all.

## paragraph.rs → text.dart (`TextStyle`, `ParagraphStyle`, `ParagraphBuilder`, `Paragraph`), fonts.rs

- Change: fonts are explicit — `Platform::font_source` is the host's font lookup, and `ParagraphBuilder::build` shapes against a `FontCollection` the caller passes.
  Reason: platform — Flutter's engine holds one font manager per process; valo shapes against a collection the caller owns.
  Affect: two Apps in one process can shape the same text against different fonts, and a host that supplies no source renders no text.

- Change: `SystemFontSource` answers `CupertinoSystemText` and `CupertinoSystemDisplay` from the platform's user-interface font on demand, where the engine registers those names into its font manager at startup.
  Reason: platform — a host with its own lookup (a guest across a boundary) passes a font manager instead of the platform's.
  Affect: Cupertino text shapes with the system font on every platform that has one.

- Change: `Platform::default_font_families` asks the host for `txt::GetDefaultFontFamilies()`, by default the engine's answer on the OS this build targets, where Flutter's engine has its own OS's answer compiled in.
  Reason: platform — one build can run on several systems, as a WebAssembly component does, and only its host knows at run time which one it is on.
  Affect: such a build draws text that leaves `font_family` unset in the default family of the system it runs on, not of one fixed when it was compiled.

- Change: `set_default_font_manager` registers the platform's default family as the collection's fallback chain, as Skia's `FontCollection::setDefaultFontManager` does.
  Reason: platform — valo has no default font manager, so an empty `families` list would walk fallbacks and land on `FontId(0)`.
  Affect: text that leaves `font_family` unset paints in the platform UI font instead of whichever face happened to be registered first.

- Change: `Paragraph` and `ParagraphBuilder` wrap valo's, with offsets in UTF-16 code units; box styles are ignored, and the ideographic baseline is the first line's bottom.
  Reason: platform — valo's paragraph has no box styles, per-box direction for a range or ideographic metrics.
  Affect: a range's `TextBox::direction` is the paragraph's rather than the run's (a placeholder's is its run's), and `BoxHeightStyle::Strut` and `Max` measure as `Tight`.

- Change: a run carries no decoration style, font features or variations, baseline or leading distribution; a combined decoration paints underline, else overline, else line-through, and an unset `font_size` shapes at valo's 14.
  Reason: platform — those are not valo span fields.
  Affect: setting them on a `TextStyle` changes nothing on screen.

## platform.rs → dart:ui `platform_dispatcher.dart`

- Change: `Platform` is `Any`, and `downcast_ref` on the trait object hands an app its host's own type; `PlatformDispatcher` is one class on every platform.
  Reason: platform — what a host offers beyond the interface (a picture from an `IOSurface`-backed buffer, its renderer's image context) belongs to that host, and naming each system's kinds in the interface would close it to the next system.
  Affect: the interface carries only what every host can answer, such as `import_pixels` for `decodeImageFromPixels`; an app that wants more names its host crate and downcasts.

- Change: Dart's isolate-global `PlatformDispatcher` is a host-supplied `Platform` that `App` holds; frame, clock and view lookup are requests on that object, not a callback table the framework assigns into.
  Reason: platform — the host supplies `Platform`, and there is no isolate-global dispatcher.
  Affect: nothing can install a dispatcher callback behind the host's back, and two Apps in one process answer independently.

- Change: there is no implicit view: `Platform::views` lists the views the platform gives, such as a phone's screen, and those the app asked for with `Platform::request_view`, each of which arrives carrying its request's id, where Flutter's engine gives an implicit view and its experimental windowing API builds window controllers over further views.
  Reason: platform — on every platform that can make a view for the app, asking is a request whose view arrives later, as iOS scenes and Android activities do, so one set of views and a request cover a phone, a desktop and an embedder alike.
  Affect: an app on a desktop has no view until it asks; the framework tells the views it asked for from those it was given by their request ids.

- Change: a requested view's window is made from a `WindowConfig` (size, position, decorations, level, activation, every desktop, background, shadow) and reached through `View::window` as a `HostWindow`, its frame, visibility, closing and native handle, as `NSView.window` is, where Flutter builds regular, dialog, popup, tooltip and satellite controllers and its `FlutterView` names no window.
  Reason: platform — the common configuration and the raw handle cover what a Rust app configures itself, as winit and wgpu expose theirs.
  Affect: an app names its windows' properties directly rather than choosing an archetype; a property the config lacks is set through the native handle; a host without windows refuses requests.

- Change: an app adds a view on a composition layer of its own through the platform's view embedder (`Platform::view_embedder`, `ViewEmbedder::add_view`), which arrives carrying its request's id as a window's view does, gives it its metrics, says whether it is in sight and removes it (`set_view_metrics`, `set_view_in_sight`, `remove_view`), and sends any view pointer reports and focus changes (`send_pointer_reports`, `send_view_focus_event`), where these are Flutter's embedder API (`FlutterEngineAddView`, `SendWindowMetricsEvent`, `RemoveView`, `SendPointerEvent`, `SendViewFocusEvent`), which no Dart code calls.
  Reason: platform — an app that shows a view in a place it makes itself, as WaOS shows a prompt in a program's window, is that view's embedder, and places its layer and passes it input as Flutter's embedder does.
  Affect: the view shows only where the app places its layer, at the size and in the sight the app says, takes the input the app forwards, under device numbers no other stream uses; a host that cannot draw a view into a layer the app places has no view embedder.

- Change: a view the platform gave may be in a window the app manages (`View::window`), and a view closes only through its window, where Flutter's implicit view has no window and its windows close through their controllers.
  Reason: platform — a host such as WaOS shows a given view in a window the app may move, title and close, as an iPad scene and an Android desktop window are, while a phone's screen, a web page's element and a host app's view are in nothing the app may close.
  Affect: closing a given view in a window closes the window; a view with no window is not the app's to close, and its way out is to quit.

- Change: a window says whether it is minimized and whether it is occluded, and the host tells the app each time either changes or the window moves, where Flutter's controllers ask each platform whether a window is minimized, hear its changes from that platform's own windowing, and have no word for a covered window.
  Reason: platform — the host is what hears a window minimized, covered or moved, and an app that stops drawing what nobody sees must know it of each window rather than of the whole application.
  Affect: an app tells a minimized or covered window from a shown one while its other windows show, and where the system does not say, as Windows does not say a window is covered and Wayland says neither, the window answers no.

- Change: the framework asks the host for a frame of one view, and the host gives one refresh of a screen at a time with the views on it that asked, where Flutter asks for and gets one frame of every view.
  Reason: platform — each window is on a screen with its own refresh rate and can be minimized or occluded, and a host that paces every view by one screen stops them all while that one is out of sight.
  Affect: each window draws at its own screen's refresh rate, and a minimized or occluded window draws nothing until it shows while the others keep drawing.

- Change: what a view asks of the system, its text input, its cursor and its focus, goes to the view's own host (`View::host`, `ViewHost`), where Flutter's framework sends it to the platform as a whole through its text input and mouse cursor channels and `PlatformDispatcher.requestViewFocusChange`.
  Reason: platform — what a view asks of the system is served by what holds that view, a window or another app's view as in WaOS, and not by the application as a whole.
  Affect: a cursor, a focus request or an input method reaches the host of the view it is for, and every view has one, a test's view one that records what it is asked.

- Change: `PlatformDispatcher.displays` is a host's `Screens` capability, whose `Screen` is Flutter's `Display` with a name, a frame placed on the desktop in points in place of a size in pixels, and what the system's bars leave free, and which also says where the pointer is.
  Reason: platform — a desktop app that places a window against a screen's edge or its own icon needs to know where the screens are, in the points its window frames are in.
  Affect: an app can place a window in screen coordinates, `Display.size` is the frame's size at `scale`, and a host with no screens to report answers `None`.

- Change: `FlutterView.display` is the screen id in the view's metrics, looked up in `Platform::screens`, and `None` while the view is on no screen the host can name; a refresh rate the host cannot say is `None` where Flutter's is 0.
  Reason: platform — a view holds no reference to the platform, and a host has no screen for a view without a window.
  Affect: a view's screen comes from the platform's list, and a view moved to another screen is reported as a metrics change, as Flutter's window metrics event carries its `display_id`.

- Change: `default_target_platform` and `platform_brightness` are queries on `Platform`, with no library global and no `debugDefaultTargetPlatformOverride`; the inert platform answers Android and light, as Flutter's test binding does.
  Reason: platform — the host-supplied `Platform` is the only source of truth.
  Affect: changing the target platform means swapping the host's `Platform`, and two Apps can disagree at once.

- Change: the system channels that reach the host are capability traits a `Platform` hands out, and a host that has none of one answers `None`.
  Reason: platform — there are no method channels; the host trait is the channel, so payloads cross as values rather than Dart's `_toMap` encodings.
  Affect: a caller sees an unsupported capability as a `None` it can act on, where Dart's missing-channel failure and a defaulted method are both invisible to it.

- Change: `Platform` asks the host whether its own text input turns editing keys into edits — backspace, delete, caret movement — and the default answer is no.
  Reason: platform — Flutter writes one embedder per host and settles this per target platform, where one framework here meets hosts that differ on the same platform.
  Affect: on a host that reports plain key presses, a text field's editing keys work whichever platform the host claims to be.

- Change: the host's text input also says whether the system's menus send Edit's cut, copy and paste (`TextInputHost::provides_edit_commands`), as macOS's menu bar does while it holds those items, and the default answer is no.
  Reason: platform — such a menu takes the key the framework leaves and sends the system's own command, which is what lets a host give a paste to the user's command alone.
  Affect: a text field there leaves ⌘C, ⌘X and ⌘V to the menu and acts on the `clipboard_event` it sends; elsewhere it handles them itself.

- Change: `Platform::dispatcher` hands out the host's threads as one `Send + Sync` object: any thread asks it for a turn at a time, answered by `EmbedderClient::wake`, and the framework hands it work to run off the main thread; Dart's isolate is woken by its message port and `Isolate.run` uses the VM's own threads.
  Reason: platform — the host owns the event loop and knows its system's work queues, and a task completed on another thread has no other way to give the app a turn.
  Affect: a spawned task can await a future completed on any thread and runs at the next checkpoint instead of at the next input, and `run_in_background` work lands on the system's queue where there is one.

- Change: `Platform::compositor` makes composition layers (`Compositor::make_layer`), nodes of the system compositor's tree that a producer feeds with the platform's own `HardwareBuffer`s from any thread, in place of Flutter's texture registry and platform-view plumbing.
  Reason: platform — the framework, the host and every producer share one process, so a buffer can go to the host's compositor directly.
  Affect: a host without a compositor answers `None`, and a layer embedded in a frame there shows as an empty hole.

- Change: a composition layer is made on its own and adopted by whoever places it: a view's frame, whose scene embeds it (`SceneBuilder::add_embedded_layer`), or another layer, whose `CompositionLayer::present` names its children with their placements beside its buffer, as Chromium's `SubmitCompositorFrame` names the surfaces a frame embeds.
  Reason: platform — a host that draws programs shows each program's picture as a composition layer whose own layers go beneath it, where Flutter's platform views are always the view's own.
  Affect: a layer has one parent at a time; a child shows only where its parent's last present placed it, and moves, is clipped and is scaled with its parent.

## image.rs → dart:ui `instantiateImageCodec` / `Codec` / `FrameInfo`

- Change: `Image` is a counted handle to the host's texture, with no clone and no dispose.
  Reason: language — the texture is freed when the last holder drops it, where Dart's collector needs `Image.clone` and `Image.dispose` to be told when that is.
  Affect: a picture is freed when the last holder lets go of it, so Flutter code being ported has no `dispose` call to make and no freed picture left to draw by mistake.


## restoration.rs → services `message_codecs.dart` (`StandardMessageCodec`)

- Change: `RestorationData` is a value enum over the kinds `StandardMessageCodec` can carry, and there is no encode or decode step.
  Reason: platform — the framework and the host meet at a Rust trait, not at a byte channel.
  Affect: a host stores and returns the `RestorationMap` it was given; a host that needs bytes on disk has to serialise it itself.

## views.rs → dart:ui `FlutterView` / `ViewPadding` / `ViewConstraints`

- Change: `View` is a handle to the native surface; the host owns the window and answers metrics from it, and the framework keeps no copy.
  Reason: platform — the native window is the source of truth; a framework-side copy would go stale.
  Affect: metrics read after a view-lifecycle notification are the window's current ones, never a cached frame behind.

- Change: `View::present` takes a shared `Scene`, which the host keeps.
  Reason: platform — the host is the engine here, and draws a window again from its last scene.
  Affect: a window seen again shows its last frame without the app drawing one, and a frame equal to the last draws nothing.

- Change: text input is a capability a `View` hands out, and a view with no IME behind it answers `None`.
  Reason: platform — there are no method channels; the host trait is the channel.
  Affect: a caller sees the absent IME as a `None` it can act on, where a focused field still edits from key events but nothing composing reaches the platform.

## pointer_report.rs → the embedder API's `FlutterPointerEvent` (`embedder.h`), copied into `PointerData` as `FlutterEngineSendPointerEvent` does

- Change: a host reports a `PointerReport`, whose `ReportedChange` carries only the fields its change uses (buttons, a scroll's delta, a pan-zoom's totals), where `FlutterPointerEvent` is one flat struct whose phase and signal kind say which of its fields count.
  Reason: platform — the report is all the embedder interface takes from a host about a pointer, and the enum leaves a host no field the converter fills or its change ignores.
  Affect: a host cannot set a press's identifier, a delta or `synthesized`, nor give a press a scroll delta; it reports what the system said, and the client's converter adds the rest.

## pointer_data_packet_converter.rs → engine `lib/ui/window/pointer_data_packet_converter.cc`

- Change: a move, an up, a remove, or a pan-zoom's update or end for a device the converter holds no state of is dropped, and the engine's `FML_DCHECK`s on the order of changes are not checked.
  Reason: language — the engine reads past the end of its map there, which C++ leaves undefined and Rust cannot do, and its checks run only in the engine's own unoptimized builds, where a `debug_assert!` would stop every app's debug build.
  Affect: a host that reports a press's end, or a gesture's update, for a device it never reported loses that report in every build, as the engine drops a cancel for such a device.

## client.rs → dart:ui `hooks.dart`

- Change: the isolate-global engine hooks are methods on `EmbedderClient`: one `frame` is a complete engine frame of the views one refresh names, pointer reports and key data arrive as calls, view lifecycle is a typed notification, and `key_data` returns whether the framework handled the key.
  Reason: platform — there is no isolate-global hooks table; the host drives a client without naming `App`.
  Affect: the host does not run scheduler phases or build `PointerEvent` / `KeyEvent` itself, and decides from `key_data`'s answer whether to keep propagating the native event.

- Change: `EmbedderClient::wake(elapsed)` reports that a `Platform::wake_at` deadline passed, on the clock `Frame::elapsed` uses.
  Reason: platform — the `App` owns its timers and has no event loop firing them.
  Affect: a `Timer` fires only when the host calls `wake` or delivers a frame, so a host that never wakes never runs one.

- Change: `platform_brightness_changed`, `locales_changed` and `screens_changed` carry no value; the new one is read back from `Platform`.
  Reason: platform — the host pushes a notification and the framework reads the dispatcher, as with `frame`.
  Affect: a host must update what its `Platform` answers before calling the hook, or observers rebuild against the old value.

- Change: `system_fonts_changed` is a client method, where Dart's engine posts a `fontsChange` system message that `PaintingBinding` turns into `systemFonts.notifyListeners`.
  Reason: platform — there are no method channels; the host trait is the channel.
  Affect: a host that loads faces after start-up must call this or laid-out text keeps the glyphs it had.

- Change: `TextInputClient.updateEditingValue` / `performAction` / `connectionClosed` are `EmbedderClient` methods the host calls.
  Reason: platform — there are no method channels; the host trait is the channel.
  Affect: IME edits reach `TextInput` only through these calls, in the host's own order relative to key data.

- Change: `EmbedderClient::drag_report` reports what is dragged from outside the application over a view as a `DragReport` — the entered, moved, dropped and exited phases, a physical position when the host has one, and the dragged items, the same `DataTransferItem`s a paste hands over — where Flutter has no drop hook and leaves it to plugins.
  Reason: platform — every system carries a drag's data in the model it uses for paste, and a host is the only place a drag from outside the app can come from.
  Affect: an app hears external drags of text, links, images and files through the widgets binding's observers, and a host that reports no position, as winit does, leaves the app to treat the whole view as the target.

- Change: a dragged item shows its types from the moment the drag enters, but its content is read only while the drop is reported, as on the web; a read asked for before fails as denied and one asked for after as changed.
  Reason: platform — a drag passing over an app must not hand it what is dragged, as the HTML spec's drag data store is protected until the drop, and every system lets a drop's data go once the drop is handled.
  Affect: an app starts every read it wants in its handler for the drop, and those reads may complete later.

- Change: `EmbedderClient::open_requested` hands the client what the system asks the app to open, an `OpenRequest` of files and links, at launch and while it runs; Flutter's macOS embedder hands a running app's to its plugins through `FlutterAppLifecycleDelegate.handleOpenURLs:`, iOS's through `application:openURL:options:` and `scene:openURLContexts:`, and Android's through `onNewIntent`, while a launch's arguments reach `main`, where the app reads them as here.
  Reason: platform — there are no plugins, and the app is the only listener; one call covers every system's way, whose files are the file dialogs' `PickedFile`, as Android hands one over as a `content://` document and the web's `launchQueue` as a file handle.
  Affect: an app hears what it is asked to open in one place on every system, the launch's request included, which waits for its handler.

- Change: `EmbedderClient::pop_route` answers at once whether the application went back, and the framework never tells the host in advance whether it handles back.
  Reason: platform — Dart's `popRoute` crosses a channel and cannot answer in time, which is why Flutter tells the host in advance; this call is direct.
  Affect: when the answer is no, the host does what its platform does without the app, which on Android is to close the activity.

- Change: `EmbedderClient::request_app_exit` takes an `AppExitReply` the framework answers, rather than replying on a channel.
  Reason: platform — there are no method channels; the host trait is the channel.
  Affect: the answer may come long after the call, such as once the user has chosen whether to save, and its reply posts it to the host's own loop; dropped unanswered, it answers exit.

- Change: `AppExit::quit` is `System.exitApplication` for a required exit only: a cancelable exit stays in the framework, which asks its own listeners and then quits, where Flutter's engine asks the framework back.
  Reason: platform — only the framework's answer decides, so the host has nothing to add between the question and the answer.
  Affect: a host implements quitting alone, and an app sees the same answers as in Flutter.

- Change: `EmbedderClient::view_close_requested` reports the user's request to close a view, and the host closes nothing itself, where Flutter's engine calls the window controller's delegate, whose `onWindowCloseRequested` closes it by default.
  Reason: platform — the host trait is the channel, and the framework decides each view's close.
  Affect: a window stays open until the app closes it.

- Change: a host reports the lifecycle as `AppLifecycleState` values through `EmbedderClient::app_lifecycle_state_changed`, and `Platform::initial_lifecycle_state` answers `None` where Dart has an empty string.
  Reason: platform — there are no method channels; the host trait is the channel.
  Affect: a host cannot report a state the framework does not know.

- Change: `menu_selected` reports an item of the menu bar chosen by its id, as the `Menu.selectedCallback` message does, and `dock_menu_selected` and `tray_icon_event` report the Dock menu's and a tray icon's, which Flutter has not.
  Reason: platform — there are no method channels, and each place's ids are its own, so each reports through its own hook.
  Affect: an id set in one place never reaches the handler of another.

- Change: `edit_command` reports Edit's undo, redo, select all and delete, chosen from a system's menu, for the view with the focus; no Flutter hook says so.
  Reason: platform — a provided Edit item acts on the focused view, as AppKit sends its action to the first responder, and copy, cut and paste already arrive as `clipboard_event`.
  Affect: a text field undoes, selects all or deletes when the user chooses it from the system's Edit menu, without the app handling the item.

## scene/ → dart:ui `SceneBuilder` / `Scene`

- Change: `SceneBuilder::pop` returns the engine layer, not the push.
  Reason: language — a layer that frames share cannot change, so it exists only once its children are added.
  Affect: a layer gets its engine layer when it pops.

- Change: a composition layer embedded in a frame shows beneath the frame's picture, through room cut for it at its place in paint order.
  Reason: platform — every compositor Inset targets shows a layer beneath a window's content without another buffer.
  Affect: content painted above a composition layer costs nothing extra.

- Change: an opacity above a composition layer fades the room cut for it, not the layer.
  Reason: platform — the layer lies beneath the window's picture, so only the picture can blend it with what was painted under it.
  Affect: a layer under an opacity fades into what was painted beneath it, not into what lies behind the window.

- Change: `SceneBuilder::add_video` puts a video's `VideoFrameReader` itself in the scene, where Flutter's `addTexture` puts the id of a texture in the engine's registry; `slices` takes the image of each reader's frame from the host as it draws, as flow's `TextureLayer` paints from the registry its context carries.
  Reason: platform — the framework, the host and the reader share one process, so the reader needs no registry, and a frame taken only when the host draws holds a decoder's buffer no longer than it is on screen.
  Affect: the framework never holds a frame; a host without video passes no images, and the video draws nothing there.

## Deferred

- A backdrop filter over a composition layer, which shows the layer unfiltered. Trigger: an app that needs glass over a video.
- `SceneBuilder`'s shader-mask, colour-filter and image-filter pushes. Trigger: the framework layers that call them.
- Decoding at any size but the file's own, which in Dart is `instantiateImageCodec`'s target width and height and the `getTargetSize` callback of `instantiateImageCodecWithSize`. Trigger: `ResizeImage`, which is what the image widget's `cacheWidth` and `cacheHeight` become.
- Reading an image's pixels back (`Image.toByteData`). Trigger: golden tests.
- The colour space an image carries beyond sRGB, so a photograph in a wider one is drawn as though it were sRGB. Trigger: a display that shows more colours than sRGB.
- Making an image from pixels that were never encoded (`ImageDescriptor.raw`, `decodeImageFromPixels`). Trigger: a caller holding pixels rather than a file.
- How far along a decode is, which Dart's codec reports as it reads the file. Trigger: a provider that reports progress.
- `StrutStyle`, `TextStyle.locale` / `ParagraphStyle.locale`. Trigger: strut, locale-specific glyphs; valo has neither.
- `ImageFilter.blur(tileMode:)` and the `dilate` / `erode` / `matrix` / `shader` filters. Trigger: valo growing those ops.
- `PlatformDispatcher.locale`, the first of `locales`. Trigger: a caller of the single-locale getter.
- The system font's optical size by text size, which CoreText applies for the engine. Trigger: valo-text setting variation axes from the text size.
- `FontFeature` named tag constructors. Trigger: a caller that uses those factories instead of `new` / `enable` / `disable`.
- `debugDefaultTargetPlatformOverride`. Trigger: a debug switcher that must override a live host without swapping `Platform`.
- `PointerData.respond` / `onRespond`. Trigger: a web host, where they call `preventDefault` on the originating DOM event.
- A report's `embedder_id`, which Flutter's Android embedder sets to the `MotionEvent`'s id so that a platform view can take back the system's touch, and every other embedder leaves at 0. Trigger: a platform view that takes back the system's touch.
- Semantics callbacks. Trigger: semantics.
- `PlatformDispatcher` callback setters and Zones. Trigger: a requirement to expose dart:ui callbacks independently of `Shell`.
- `_updateFrameData` and the `frameNumber` argument to `_beginFrame`. Trigger: `FrameData`.
- The rest of `hooks.dart`. Trigger: the matching dart:ui callback.

## clipboard.rs → `services/clipboard.dart`'s `Clipboard.setData`, `getData` and `hasStrings` on the platform channel

- Change: the host's clipboard holds items, each offered in several MIME types, given or promised, files given or promised, and how a copy is to be treated, where Flutter's channel carries plain text.
  Reason: platform — every system's clipboard holds items in several types, which an app reads and writes as the system's own apps do.
  Affect: an app can copy an image or a file, and read what another app copied in any type the system names.

- Change: a promised file produces its bytes into a sink the host hands it (`FileSink`), and the host writes them where the receiver chose.
  Reason: platform — Windows and Android take a promised file as a stream, which macOS's write at a path is a copy of, while a path would need a temporary file to stream.
  Affect: an app promising a file never learns where the receiver put it, and one in a sandbox writes nothing outside it.

- Change: the host tells the client when the clipboard changed (`clipboard_changed`) and when the user copied, cut or pasted through the platform's own command (`clipboard_event`), which Flutter's engines never say.
  Reason: platform — where only the user's own paste may read the clipboard, the paste's content comes with the host's word of it.
  Affect: the framework learns of a copy made elsewhere, and pastes what the user pasted even where it may not read the clipboard itself.

## menus.rs → services `system_channels.dart`'s `SystemChannels.menu` (`Menu.setMenus`, `Menu.selectedCallback`), `platform_menu_bar.dart`'s `PlatformProvidedMenuItemType`

- Change: a menu is a value of `MenuEntry`s (the app's items, the system's provided items, submenus and separators) set whole at each place the system draws menus: the menu bar (`Platform::menu_bar`), a menu at a point in a view (`popup_menus`), the Dock icon's (`dock_menu`) and a tray icon's; Flutter's `flutter/menu` channel carries the menu bar alone, as maps.
  Reason: platform — every place a system draws a menu takes the same tree, and there are no method channels.
  Affect: an app builds each menu from its state and sets it again when the state changes, and a place the system lacks answers `None`, where the app draws its own.

- Change: an item carries a check (a checkbox on, off or mixed, or a radio on or off) and an icon, which Flutter's serialized `PlatformMenuItem` does not.
  Reason: platform — AppKit, Win32 and the DBus menu draw both, and only a menu the framework draws itself could draw them otherwise.
  Affect: a native menu shows check marks and icons; Windows, which has no mixed state, shows a mixed one unchecked.

- Change: `ProvidedItem` adds Edit's undo, redo, cut, copy, paste, select all and delete to Flutter's provided items, and leaves out the speaking ones; each acts as the system's own, Edit's sent to the focused view as `clipboard_event` and `edit_command`, and an item the system has no counterpart for is left out of its menu.
  Reason: platform — a system knows its Edit menu by its standard items, as macOS adds dictation and emoji to it, and a host never stands in for an item its system lacks.
  Affect: Edit's items work in every text field without the app handling them, and the same menu shows fewer provided items on Windows and Linux than on macOS.

- Change: the macOS menu bar shows the system's standard menus, the application's, Edit and Window, until the app sets its own, where Flutter's macOS embedder shows the one in the app's `MainMenu.xib`.
  Reason: platform — an app here has no nib, and one without a menu bar would have no Quit, Hide or Edit.
  Affect: an app that sets no menu bar still quits with ⌘Q and copies with ⌘C, and one that sets its own replaces the standard menus whole.

- Change: `PopupMenus::show` shows the system's own menu at a point in a view and answers the chosen item's id once it closes, as a future; Flutter has no such call, only in-view context menus and the macOS menu bar.
  Reason: platform — a window narrower than its menu, such as a panel at a screen's edge, cannot hold a menu drawn inside its view.
  Affect: the menu is the system's, drawn outside the window, and Linux answers `None`: its desktops have no menu of the system's, as GTK and Qt draw theirs in a pop-up surface, which winit offers from 0.31, where a framework can draw them.

- Change: `TrayIcons::add` shows an icon in the system's tray with an image and a tooltip, removed when its handle drops, and reports what the system tells of it as `TrayIconEvent`s; its menu is one the system opens by itself (`attached_menu`, macOS and Linux) or one the app opens at it (`menu_at_icon`, macOS and Windows), each `None` where the system has not; Flutter has no tray.
  Reason: platform — each system offers some of these and decides when a tray's menu opens, so a convention such as Windows' right click opening a menu is the app's.
  Affect: an app on Windows opens a menu when it hears `ContextMenuRequested`, and on macOS a click on an icon with an attached menu opens the menu and reports nothing.

- Change: `TrayIcons::add` answers the icon as a future once it exists, or a `TrayIconError` where the system did not show it, as `PopupMenus::show` and `Clipboard::write` answer theirs; the icon then says which menus it offers.
  Reason: platform — a host may make the icon elsewhere, on another thread or in another process, as WaOS makes a program's, and a system may refuse it, as a Linux desktop's bus can.
  Affect: an app has an icon, and knows which menus it offers, only once the future answers; a native host answers at once.

- Change: `MenuBar::perform_key_equivalent` takes a key-down the app claimed when it came and leaves later, as Chrome's browser process sends on to the menu a key its page did not consume; Flutter's engines answer every key at once.
  Reason: platform — AppKit wants a key's answer before its call returns, and an app that waits on another thread, as WaOS waits on a program, answers later.
  Affect: a key a program leaves still chooses the menu bar's item, from the system's own event, until the host lets go of that event a few seconds later.

- Change: `DockMenu::set_menu` sets the items macOS adds to the Dock icon's menu; Flutter has none.
  Reason: platform — AppKit asks the application's delegate for it, which only the host is.
  Affect: an app's Dock menu is set whole like any other, and every other system answers `None`.

## opener.rs → the `url_launcher` package's `launchUrl` and `canLaunchUrl`

- Change: opening is a host capability, `Platform::opener`, with a call for each kind, `open_link`, `open_file` and `reveal_file`, as Electron's `shell` and Tauri's opener have them, where `url_launcher` takes one URI.
  Reason: platform — phones share a file through the system (Android's `FileProvider`, iOS's `UIDocumentInteractionController`) rather than by a `file://` URI, and only a file can be revealed.
  Affect: an app names a file by its path, and a host without a file manager answers `reveal_file` with a failure.

- Change: `Opener::can_open_link` answers `None` where the system will not say, where `canLaunchUrl` answers false.
  Reason: platform — iOS answers only for the schemes the app declares, Android only for the intents its manifest's `<queries>` names, and Linux's portal not at all, since asking about many schemes tells which apps the user has.
  Affect: an app learns that nothing opens a link when opening it fails with `OpenError::NoApp`.

- Change: opening answers `Result<(), OpenError>` through a future, `NoApp` where no app takes the link or file, where `launchUrl` answers a bool.
  Reason: platform — the systems tell no app from another failure, and Windows' and Linux's ask a service that answers later.
  Affect: an app can tell the user that nothing opens a kind of link or file.

## notifications.rs → the `flutter_local_notifications` package's `show`, `zonedSchedule`, `cancel`, `requestPermissions` and `onDidReceiveNotificationResponse`

- Change: notifications are a host capability, `Platform::notifications`, and a notification is a value posted under a string id, which a later post under the same id replaces, where the plugin shows one under an int id over method channels.
  Reason: platform — there are no plugins, and each system's own model is a notification under a name of the app's (a request's identifier on Apple's, Android's tag, the web's tag, a Windows toast's tag, Linux's `replaces_id`), which a string reaches on every one.
  Affect: an app names its notifications by strings, and a host where the system would not show the app's answers `None`, as macOS does for a program outside an application bundle.

- Change: a typed reply (`replies`), delivery at a later time (`scheduling`) and the user's permission (`permission`) are capabilities, each `None` where the system lacks it, and a reply action is made only through `NotificationReplies::reply_action`, where the plugin offers each everywhere and fails where the system has none.
  Reason: platform — Linux and the web hold no notification for later, Linux takes a reply only where the server advertises KDE's `inline-reply` and the web none without a service worker, and Linux keeps no permission for an app.
  Affect: an app offers a reply field or a reminder only where `replies()` or `scheduling()` answers, and has a reply action only there.

- Change: `post` and `schedule` never ask the user: where the system does not let the app notify, because the user was never asked or said no, they answer `NotificationError::Denied`, and the app asks first with `NotificationPermission::request`, the plugin's `requestPermissions`; the plugin's `show` answers nothing either way.
  Reason: platform — no system's own post asks (`UNUserNotificationCenter`'s `add` fails with `notificationsNotAllowed`, Android 13's `notify` and the web's `Notification` show nothing), and only the system's own request does.
  Affect: an app asks before its first post, and learns from a post's answer that it was not shown.

- Change: a click on a notification, one of its actions or a reply reaches the app as `EmbedderClient::notification_activated(NotificationActivation { id, action, reply })`, the click that started the app included, where the plugin hands a running app's to `onDidReceiveNotificationResponse` and the launch's to `getNotificationAppLaunchDetails`.
  Reason: platform — there are no plugins, and the client is the only listener; the launch's click arrives once the client exists, as a launch's open request does.
  Affect: an app hears every click in one place, by the notification's id and the action's, the launch's waiting for its handler.

## file_dialogs.rs → the `file_selector` package's `openFile`, `openFiles`, `getDirectoryPath`, `getSaveLocation`, and cross_file's `XFile`

- Change: the system's file dialogs are a host capability, `Platform::file_dialogs`, with one function a kind, where Flutter reaches them through the `file_selector` plugin over method channels.
  Reason: platform — there are no plugins and no method channels; the host trait is the channel.
  Affect: an app asks the platform for its dialogs, and a host without them answers `None`.

- Change: a pick is a `PickedFile`: a path where the system gives one, the system's handle where it gives none (the web; Android where the document seeks), and a stream where the system gives a pipe (Android), where Flutter's `XFile` holds a path or a blob, reading only; `PickedFile::read_all` is `XFile.readAsBytes`.
  Reason: platform — Android's documents and the web's files have no path, and a copy into the app's cache could not be written back.
  Affect: an app reads and writes a handle at any offset where the system gives no path, and reads a stream through once.

- Change: `PickedFile::url` answers the URL the system reads a pick by: a percent-encoded `file:` URL for a path, Android's `content:` URI, the web's `blob:` URL, where `XFile.path` is a path or a blob URL by platform.
  Reason: platform — every system's player and every API that takes a URL reads a pick by it, and a pick without a path has a URL all the same.
  Affect: an app hands any pick to a player, or another API that takes a URL, without reading it first.

- Change: a folder pick is a `PickedFolder`: a path where the system gives one, and the system's folder handle where it gives none (the web's directory handle, Android's document tree), which lists the folder, opens its files and folders by name, and makes them when writable, where `getDirectoryPath` answers a path or nothing.
  Reason: platform — neither the web nor Android gives a folder a path, and a copy could not be written back.
  Affect: an app reaches a picked folder's files through the handle on the web and Android, and through the path elsewhere.

- Change: an open pick grants reading only unless the app asks to write (`OpenOptions::writable`), and a save grants writing to the one file chosen; `file_selector` has no such option.
  Reason: platform — Android grants write only with `FLAG_GRANT_WRITE_URI_PERMISSION`, and the web only when asked for `readwrite`.
  Affect: a handle opens for writing only when the pick asked to write.

- Change: every dialog takes the parent view whose window it belongs to, which macOS shows it as a sheet on.
  Reason: platform — a file dialog belongs to a window, which a view-based interface names by its view.
  Affect: a dialog opened for a window stays with it on macOS, and stands alone without one.

## media.rs → the `video_player` package's `VideoPlayerController` and the `camera` package's `CameraController`, through their platform interfaces

- Change: players and cameras are host capabilities, `Platform::players` and `Platform::cameras`: a player is one host object made from a URL, a link or a pick's `PickedFile::url`, loading by itself, controlled and observed through its state, and a camera is opened, asking the user, then started as a session with an output per use, where Flutter's plugins have controllers that are initialized over method channels.
  Reason: platform — the host is the system's media stack, and its players and capture sessions are that model on every system Inset targets.
  Affect: an app makes a player and calls it at once, without awaiting an initialization, awaits a camera's opening, which asks the user the first time, and its start, which answers once the session runs or is interrupted, and a system without players or cameras answers `None`.

- Change: a player's picture goes to one destination at a time, which the app makes and gives it with `set_video_output`, as Media3's `setVideoSurface`: a composition layer, whose content becomes a layer the system draws into, or a frame reader made on its own from `Platform::video_frame_readers`, handing out `VideoFrame`s; a camera takes a layer for its preview and a reader for its frames the same way, among its outputs, where Flutter's plugins register a texture the engine copies from.
  Reason: platform — protected and HDR video show only where the system draws them itself, Windows' and Android's decoders feed one output, and an app makes the reader itself as it makes Android's `ImageReader` or Apple's `AVPlayerItemVideoOutput`.
  Affect: a player shown on a layer has no frames to read, one sent to a reader shows on no layer, and a reader given to a second source reads that one alone.

- Change: a `VideoFrame` is one picture as the system handed it out, which tells its size and makes a wgpu texture of itself on the device the app gives, the texture holding the frame, or none on a device that cannot take the frame's memory, where a Flutter app holds no frame: the engine takes each from a texture a plugin registered and draws it itself.
  Reason: platform — every system hands out a frame that holds its place in the decoder's pool and that its GPU makes a texture of with no copy, Apple's `CVPixelBuffer` and Android's `Image` among them, and a host whose frames are not such a type, such as one that runs the app as a WebAssembly program, makes the texture its own way.
  Affect: an app draws any host's frame as a texture on its own device where the device takes the frame's memory, such as a Metal device on macOS, and a kept frame, or a kept texture of one, keeps the decoder from reusing that picture's memory.

- Change: a player lists its media's tracks in its state, plays the audio track and shows the text track the app selects, tells the media's tags as `MediaMetadata`, and hands out the `Cue`s of its text track as the media styles them, Media3's `Cue` with a field the media leaves unsaid `None`, beside the user's caption style, which the platform's caption settings answer, each of whose settings says whether it applies always or only where the media says nothing, where the `video_player` plugin has no tracks or tags and reads captions only from a file the app supplies.
  Reason: platform — every system's player lists tracks and selects one audio and one text track or none, and gives the subtitles it would draw as data with the media's own styling and place (Media3's `Cue`, Apple's legible output at `SourceAndRulesOnly`), and every system with a caption style gives it as settings of its own (Apple's `MACaptionAppearance`, Android's `CaptioningManager`, Windows' `ClosedCaptionProperties`).
  Affect: an app reads both the media's styling and the user's, merges them as it draws, as the framework's `SubtitleView` does, and knows a media without a video track has no picture.

- Change: `Platform::caption_settings` answers the user's caption settings where the system keeps them, whose `style` is the style the user set and whose `changed` resolves at its next change, and `None` where the system keeps none, as on Linux and the web; dart:ui has no caption settings.
  Reason: platform — Apple's `MACaptionAppearance`, Android's `CaptioningManager` and Windows' `ClosedCaptionProperties` each give the user's style and tell when it changes, and Linux and the web keep none.
  Affect: an app reads the user's caption style and follows its changes where the system keeps one, and draws subtitles in the media's own style where it keeps none.

- Change: a player's position is read when the app needs it (`MediaPlayer::position`), and `changed` resolves when it jumps, as a seek landing or a loop makes it, but not as it moves on while the player plays, where the `video_player` plugin copies the position into its value on a timer of its own.
  Reason: platform — every system lets an app read the position (AVPlayer's `currentTime()`, Media3's `getCurrentPosition()`, Windows' `PlaybackSession.Position`, the web's `currentTime`) and tells it when the position jumps (Apple's `AVPlayerItemTimeJumped`, Media3's `onPositionDiscontinuity`, the web's `seeked`), and none sends it on a timer.
  Affect: an app that shows the position reads it at each frame it draws, as exact as the system's own clock, and hears nothing from the player while it plays on.

- Change: a player's state says what the system's player does, paused, waiting or playing, with the reason where the system gives one, where the `video_player` plugin's value has `isPlaying`, which says what the app asked, and `isBuffering`.
  Reason: platform — the system pauses a player itself and tells why (Apple's `timeControlStatus` and rate change reasons, Android's audio focus and noisy audio), so what the app asked for is not what plays.
  Affect: an app shows a play button once a call pauses its video, a spinner only while the player waits for data, and a replay button at the end.

- Change: the state also tells a seek under way until it lands, the parts it can seek into, the mute it plays with, and a failure's kind at any time, a seek takes how far from its target it may land, and the state's rate and volume are the system's, where the plugin's value has the buffered ranges, an error description, a rate and a volume, and its seeks are exact.
  Reason: platform — every system's player tells these, the systems change the rate and the mute themselves, and Apple's and Media3's seeks take a tolerance.
  Affect: an app greys its scrubber where the media cannot seek, shows a seek until it lands, words a failure in its own language, and seeks quickly while the user drags.

- Change: `Cameras::changed` answers a future that resolves at the next change of what `Cameras::cameras` answers, such as a camera plugged in or out, after which the app lists the cameras again and asks for a new future, where the `camera` plugin's `availableCameras` answers the list once and tells no change.
  Reason: platform — every system Inset targets tells an app when its cameras change: Apple's discovery session, Android's `CameraManager.AvailabilityCallback`, the web's `devicechange` event and Windows' `DeviceWatcher`.
  Affect: an app keeps its list of cameras current as cameras are plugged in and out.

- Change: a camera lists its formats, each a picture size with the frame rates it keeps and the sizes it takes photos at, and starts in one of them with all its outputs given at once as `CameraOutputs`, its session telling the format it runs in and whether it runs, is interrupted, for a reason where the system gives one, or ended for an error, where the `camera` plugin takes a resolution preset and adds outputs to a controller it has initialized.
  Reason: platform — every system configures a camera's outputs together and describes what a camera can run before it runs (Apple's device formats, Android's stream configuration map), and a system that runs a camera in one format at a time runs every session on it in that format.
  Affect: an app picks a format from the camera's own list and gives every output as it starts, a session on a camera another session already runs reports that session's format, and an interruption, such as another app taking the camera, ends by itself, its reason unknown on a Mac.

- Change: a camera's photos are answered encoded by the system at a size from the format's list, as bytes and their MIME type, and its recordings written by the system to a path the app gives, where the `camera` plugin answers an `XFile` in a folder of its own.
  Reason: platform — the system encodes and writes them, and an app chooses where its files go.
  Affect: a photo arrives as JPEG bytes at the size asked for, and a recording is a QuickTime movie at the app's path.