app_settings_ai 0.1.0
app_settings_ai: ^0.1.0 copied to clipboard
On-device AI settings proposals for the app_settings plugin. Apple Foundation Models on iOS, Gemini Nano on Android. Free and MIT licensed.
app_settings_ai #
On-device AI settings proposals for the
app_settings plugin. Free and MIT licensed.
Turns a natural-language complaint — "I'm not getting message alerts" — into a ranked list of settings screens to open, with a short rationale for each. Inference runs entirely on the device: Apple Foundation Models on iOS 26+ (Apple Intelligence devices) and the ML Kit GenAI Prompt API / Gemini Nano on supported Android devices. Nothing leaves the device.
This is a separate package on purpose. app_settings stays dependency-free, so apps
that just want to open a settings screen don't pay for an on-device model runtime or a
raised SDK floor.
Installation #
flutter pub add app_settings_ai
app_settings comes along as a dependency; import whichever you need.
iOS requires Xcode 26+ to build the real implementation and, like app_settings,
Swift Package Manager:
flutter config --enable-swift-package-manager
Usage #
import 'package:app_settings_ai/app_settings_ai.dart';
// Gate your UI on this: many devices on both platforms report unavailable.
final availability = await AppSettingsAI.availability();
if (availability.canDownload) await AppSettingsAI.prepareModel();
final result = await AppSettingsAI.proposeSettings(
request: "I'm not getting message alerts",
context: {'notificationsEnabled': false},
allowedSettings: [
AppSettingsType.notification,
AppSettingsType.batteryOptimization,
AppSettingsType.wifi,
],
);
if (result.isOk) {
for (final proposal in result.proposals) {
print('${proposal.title} (${proposal.setting.name}) — ${proposal.rationale}');
// await proposal.open();
}
}
proposeSettings never throws. A device that cannot run the model yields
SettingsProposalStatus.unavailable with a unavailableReason; a failed generation
yields SettingsProposalStatus.error. Check result.status rather than catching.
Both native handlers test capability themselves before generating, so there is no separate round trip to check it first.
Running the example #
cd example && flutter run
Nothing to configure. availability() works everywhere; generating real proposals
needs a device that supports on-device inference. See
example/lib/main.dart for the availability card and
proposal list.
How the two platforms are kept consistent #
Both platforms receive the same instructions and prompt, built in Dart, and return
the same payload shape:
{
status: "ok" | "unavailable" | "error",
provider: "apple_foundation_models" | "mlkit_gemini_nano",
reason?: unsupportedOsVersion | deviceNotEligible | notEnabled |
modelDownloadable | modelDownloading | modelNotReady | unknown,
message?: String,
proposals: [ { setting: String, title: String, rationale: String, confidence: double } ]
}
Dart then does all the validation in one place — dropping unknown or duplicate setting keys, clamping confidence, sorting by confidence and capping the count — so iOS and Android are held to identical rules.
Requirements and caveats #
- Gate your UI with
availability(). Many devices on both platforms report unavailable, and App Review / Play reviewers need a supported device to see the feature. - iOS: needs Xcode 26+ to build the real implementation.
FoundationModelsis weak-linked, so the plugin still loads and runs on systems without it; those devices reportunsupportedOsVersionrather than failing to launch. In practice the floor is iOS 15: the iOS 27 SDK in Xcode 26 setsMinimumDeploymentTargetto 15.0, so no app built with a current Xcode can deploy lower, whateverPackage.swiftdeclares. The package itself still declares.iOS("13.0")for older toolchains, where the handler compiles as unavailable. - Android:
genai-prompttargets API 26+. This plugin keepsminSdk 16and reportsunsupportedOsVersionbelow 26. Inference must run while the app is in the foreground (BACKGROUND_USE_BLOCKED); AICore also enforces a per-app quota and blocks devices with an unlocked bootloader. - Output differences: iOS uses guided generation, so its structure is guaranteed.
Android asks Gemini Nano for JSON and parses it; malformed output returns
SettingsProposalStatus.error.
License #
MIT. See LICENSE.