Contributing
Setup
- Flutter and Dart through mise:
mise installreadsmise.toml. Every Dart/Flutter command below ismise exec -- .... - Rust through rustup, plus
cargo install cbindgen(only for C ABI changes). - A C toolchain (Xcode command line tools / NDK) for the app link step.
mise exec -- dart pub get # resolves the whole workspace, melos included
Repo shape
pubspec.yaml pub workspace + melos config (`melos:` key)
Cargo.toml Cargo workspace: every crate under packages/*/rust (and slint_build/interpreter)
packages/ the Dart packages, each with its Rust crate(s) inside
examples/ apps consuming the packages through the workspace
The root README.md describes each package. Full package/example docs live
under site/content/ (this site). Package and example README.md files are
short pub.dev overviews. Each package also has an AGENTS.md (invariants and
traps, for humans and AI agents alike).
Everyday commands
The short form is just (installed by mise): just lists the recipes,
just check runs what CI runs, just bindings slint_testing regenerates a
package's FFI bindings. examples/todo/justfile has the release builds and
the tree-shaking check (just build-macos, then just verify-treeshake);
examples/todo_skia/justfile the two things that run locally there.
Underneath, melos is a root dev dependency; run it with dart run:
mise exec -- dart run melos run analyze # dart analyze, per package
mise exec -- dart run melos run test # Dart packages, then Flutter packages and examples/todo
mise exec -- dart run melos run codegen # ui/*.slint → lib/*.g.dart in the examples
mise exec -- dart run melos run rust:clippy # every crate except slint-skia-ffi
mise exec -- dart run melos run rust:test # cargo test, same exclusion
mise exec -- dart run melos run check # everything CI runs
mise exec -- dart run melos list # what's in the workspace
melos run with no script name lists the scripts and their descriptions.
Or go direct — cd packages/<pkg> && mise exec -- dart test, cd examples/todo && mise exec -- flutter test; see AGENTS.md for the full list.
First runs are slow: flutter test and slint_testing's dart test build
their Rust crates through the native-assets hooks (release profile, minutes).
Nothing needs a manual cargo build.
What default CI skips (Skia)
slint_skia's crate pulls in all of Skia (10+ GB of artifacts). Default CI
(melos run check, just check) excludes slint-skia-ffi from
cargo clippy / cargo test and examples/todo_skia from flutter test.
The skia-* jobs in .github/workflows/ci.yml compile it instead, one per
platform family (skia-apple, skia-linux, skia-android, skia-windows):
clippy and the crate's GPU test (a frame rendered into the platform's texture
and read back) where the runner can run them, and a debug build of
examples/todo_skia, which compiles the slint_skia plugin too. They are
the only place the GPU path is built and checked; no local check builds it.
For todo_skia locally, dart run build_runner build and dart analyze .
are the everyday checks. flutter run, flutter build, and flutter test
there compile Skia through slint_skia's hook — optional, slow, and not part
of the default check.
Dead code and size
dart analyze already flags unused locals, fields, and imports. For
anything wider:
cargo install cargo-machete cargo-bloat
mise exec -- dart pub global activate dart_code_linter
mise exec -- dart run melos run deps:unused # cargo machete: unused Rust dependencies
mise exec -- dart run melos run dart:unused # dart_code_linter check-unused-code, per package
dart:unused reports the builders and hook entry points (slintBuilder,
buildSlintAot, ...) as unused — they are reached through build.yaml and
the apps' hook/*.dart, not by Dart imports. cargo machete lists
slint-skia-ffi's Skia dependencies: the skeleton does not reference them
yet (see its AGENTS.md).
Release size is the AOT dylib; the slint_compiler
page has the measured breakdown ("Where the bytes go") and the cargo bloat recipe.
Before opening a PR
mise exec -- dart run melos run checkis green.- A release build of the example still tree-shakes when the change touches
codegen or hooks:
cd examples/todo && FLUTTER_RECORD_USE=true mise exec -- flutter build macos --release(seepackages/slint_compiler/AGENTS.mdfor what to verify in the bundle). - Generated files (
*.g.dart,*.aot.g.dart,bindings.g.dart,rust/include/*.h) are regenerated, not hand-edited, and committed. - Docs in
site/content/(and the short package/example README overview, if the blurb changed) are updated in the same change; a new invariant lands in that package'sAGENTS.md.
Commit messages follow conventional-commit style (feat:, fix:, test:,
docs:, refactor:), imperative subject, body explaining the why. The
human is the only author: no Co-Authored-By trailer, "Generated with …"
line or AI agent as author on a commit, merge or PR, whatever a tool
defaults to.
Adding a package
- Create it under
packages/<name>/withpublish_to: noneandresolution: workspace. - Add
packages/<name>to the rootpubspec.yamlworkspace:list; a Rust crate goes into the rootCargo.tomlmemberswith[lints] workspace = truein its ownCargo.toml. - Write its short
README.md, full docs undersite/content/packages/, andAGENTS.md; add a row to the root README's layout table. - If it ships a native library: a
hook/build.dartthroughslint_build,ffigen.yaml+rust/cbindgen.toml, and the generated bindings committed.