Skip to main content

Build to a device

The Android emulator has no arm64 native libraries for these backends, and MLX only runs on physical iOS hardware. A simulator tells you the app launches and nothing more.

Register a backend before initializing

Use the right generation method

Dart has no overloading, so there are four: Passing messages to generate does not compile.

Cancel subscriptions in dispose

A running generation outliving its widget is the most common bug in a Flutter integration, and it shows up as setState after dispose rather than as anything to do with the SDK.

Check mounted after every await

Guard deviceId

RunAnywhere.deviceId throws before initialize(). Read it behind isReady.

Cap output length

maxOutputTokens is the biggest lever on latency and battery.

Unload what you are not using

Holding a language model, a speech model, and a vision model at once on a phone is how you get insufficientMemory.

Remember the positional optionals

models.list(filter) and models.unloadAll(category) take positional parameters, not named ones. lora.remove(adapterId) does too.

One RAG session at a time

The index is single-session. Opening a second while one is open throws. Close the first.

Rebuild narrowly while streaming

StreamBuilder around a large subtree rebuilds all of it on every token. Accumulate into a ValueNotifier and wrap only the text widget in a ValueListenableBuilder.

Pin QHexRT separately

runanywhere_qhexrt publishes at 0.20.19 while the rest are at 0.20.24. Aligning the versions breaks pub get.

Keep the Hugging Face token out of source

Use flutter_secure_storage or the platform keychain, never a constant in the repo.