Flutter iOS local AI recipes
Here are small tasks you can adapt to your Flutter app: summarize a passage,
extract appointment fields and classify a note. Start with
installation and availability. Each recipe uses
ModelMode.local and CloudPolicy.never to keep generation on the device.
The snippets were reviewed against the source and were not executed.
Summarize a short passage
Use the complete availability-checked, session-owned example in the English tutorial or Spanish tutorial. Use supplied facts, bound input and output, and keep the manual path available.
Extract a person’s name and an explicitly supplied day
Pass a schema instead of relying on a prompt that merely requests JSON. This function returns a decoded map for application review, or null when the model is unavailable or the expected fields cannot be accepted.
import 'package:cupertino_fundations_models/cupertino_fundations_models.dart';
Future<Map<String, Object?>?> extractAppointment(String text) async {
final models = CupertinoFoundationModels();
try {
final availability = await models.checkAvailability(
mode: ModelMode.local,
cloudPolicy: CloudPolicy.never,
localeIdentifier: 'en_US',
);
if (!availability.isAvailable) return null;
final response = await models.generateStructured(
prompt: Prompt.text(text),
mode: ModelMode.local,
cloudPolicy: CloudPolicy.never,
instructions:
'Extract only the person and day explicitly written in the text. '
'Use an empty string for a missing field. Do not infer a calendar date.',
schema: const StructuredSchema.object(
name: 'Appointment',
properties: <String, SchemaProperty>{
'person': SchemaProperty.string(),
'day': SchemaProperty.string(),
},
requiredProperties: <String>['person', 'day'],
),
options: const GenerationOptions(maximumResponseTokens: 120),
);
final value = response.structuredValue;
if (value is! Map<String, Object?>) return null;
if (value['person'] is! String || value['day'] is! String) return null;
return value;
} on FoundationModelsException {
return null;
}
}
The facade owns session disposal for generateStructured(). A valid schema
does not prove the extracted name/day is correct. Compare with the input and
ask the user before creating an appointment. See
document extraction for a larger bounded example.
Classify text into a small vocabulary
An enum prevents arbitrary category strings. Keep an other label for text
outside the feature’s scope and validate the decoded result independently.
import 'package:cupertino_fundations_models/cupertino_fundations_models.dart';
Future<String?> classifyNote(String text) async {
final models = CupertinoFoundationModels();
const categories = <String>['work', 'personal', 'other'];
try {
final availability = await models.checkAvailability(
mode: ModelMode.local,
cloudPolicy: CloudPolicy.never,
localeIdentifier: 'en_US',
);
if (!availability.isAvailable) return null;
final response = await models.generateStructured(
prompt: Prompt.text(text),
mode: ModelMode.local,
cloudPolicy: CloudPolicy.never,
instructions:
'Classify the note as work, personal or other. '
'Choose other if the text is ambiguous or unrelated.',
schema: const StructuredSchema.object(
name: 'NoteCategory',
properties: <String, SchemaProperty>{
'category': SchemaProperty.string(enumValues: categories),
},
requiredProperties: <String>['category'],
),
options: const GenerationOptions(maximumResponseTokens: 60),
);
final value = response.structuredValue;
if (value is! Map<String, Object?>) return null;
final category = value['category'];
if (category is! String || !categories.contains(category)) return null;
return category;
} on FoundationModelsException {
return null;
}
}
The label is an AI suggestion, not an authorization or irreversible decision. Allow correction and preserve a deterministic default.
Add streaming, tools or speech
- README streaming example: cumulative
snapshots, completion, failure and
finallydisposal. - Guided streaming: only terminal JSON is final.
- Tool calling: validate authorization in the app’s tool.
- Speech transcription: separate privacy, permissions and assets.
- Example source: existing Flutter integration.
Do not run multiple requests on one session. Await cancellation and native cleanup before reuse. Use troubleshooting for typed failure recovery; do not hide failure behind generated success text.