Todo Skia
The examples/todo UI on the slint_skia backend: its own ui/todo.slint
imports the same shared list (examples/todo_shared/ui/todo_view.slint) —
minus the AOT tree-shaking canary — compiled at runtime by SkiaSlintEngine,
rendered by Skia on the GPU and shown as a Flutter external texture.
The page creates a SkiaTextureRenderTarget at the laid-out size (physical
pixels), shows it with Texture(textureId:), renders from a Ticker and
forwards pointer events. What the backend has not grown yet — callbacks — the
page reports on screen in the words of the backend's own
UnimplementedError, as it does any error from the GPU path. See the
slint_skia package, "Shortcuts &
Ceilings", for what is still a shortcut.
Building
slint_skia's build hook compiles slint-skia-ffi, which pulls in all of
Skia (10+ GB of artifacts), and each platform build compiles the slint_skia
plugin. The default check does not build either; the skia-* jobs in
.github/workflows/ci.yml build the app for macOS, the iOS simulator,
Android (arm64), Linux and Windows. The example ships no Linux or Windows
runner: those jobs generate one with flutter create --platforms=... first.
Locally, the everyday checks are:
cd examples/todo_skia && dart run build_runner build # ui/todo.slint → lib/todo.g.dart
dart analyze .
Both run without the native build.
Codegen
lib/todo.g.dart comes from slint_generator's builder, opted into ui/
by build.yaml. slint_skia has no SlintComponentFactory, so the wrapper
has no load/register/defaultFactory; the rest of the typed API is
there. main.dart writes the embedded tree to disk, so the import of the
shared list resolves, compiles its entry with SkiaSlintEngine, and hands
the component to the generated constructor:
final defs = engine.compile(
TodoApp.slintSource,
path: writeSlintTree(TodoApp.slintSource, TodoApp.slintFiles,
name: 'todo.slint'),
);
final component = defs.single.instantiate() as SkiaSlintComponent;
final app = TodoApp(component);
app.todoModel = [
for (final e in store.items) TodoItem(title: e.title, checked: e.checked),
];
The list itself lives in the shared TodoStore (examples/todo_shared,
same seed and rules as examples/todo) and reaches Slint only through
app.todoModel. main.dart registers onAddTodo/onToggleTodo/
onRemoveDone too; the backend throws UnimplementedError for callbacks
today, which the page reports as a ceiling. The wrapper's renderTarget
throws StateError for a Skia component — it renders to a texture, not a
software target — so the frame goes through SkiaTextureRenderTarget.
As in examples/todo, nothing under flutter: assets: — the source ships
only as the string the builder embedded.
What differs from examples/todo
todo |
todo_skia |
|
|---|---|---|
| Backend | interpreter (debug) / AOT (release) | SkiaSlintEngine, every mode |
| Entry point | SlintComponent.load('ui/todo.slint') |
SkiaSlintEngine().compile(...) at a writeSlintTree path, then TodoApp(component) |
| Frame | SlintView over a software render target |
Texture(textureId:) over SkiaTextureRenderTarget (GPU) |
| Input | pointer + keyboard / IME (SlintView) |
pointer only |
| Callbacks | delivered | onAddTodo & co. throw UnimplementedError; the list stays Dart-owned |
| Hooks | app-level hook/build.dart + hook/link.dart |
none — slint_skia's own hook builds its crate |
| Tests | flutter test locally |
build_runner + dart analyze locally; the builds run in CI's skia-* jobs |