Flutter Foundation Models
A Flutter plugin for Apple's on-device Foundation Models, available on iOS 26+ and macOS 26+.
Features
- Text Generation - Generate natural language responses
- Structured Output - Generate typed Dart objects with schema validation
- Streaming - Real-time streaming for progressive UI updates (text and structured)
- Tool Use - Let the model call your functions to fetch data or perform actions
- Generation Guides - Constrain output with patterns, ranges, and enums
- Model Configuration - Custom adapters, use cases, and guardrails
Requirements
- iOS 16.0+ (Foundation Models API requires iOS 26.0+ at runtime)
- Flutter 3.22+
- Xcode 26+ (for iOS 26 SDK)
Note: The package can be added to apps targeting iOS 16+, but the Foundation Models API is only available on iOS 26+. Use SystemLanguageModel.isAvailable to check availability at runtime.
Package Manager Support
This plugin supports both Swift Package Manager and CocoaPods. SPM is recommended for new projects and provides faster build times.
Installation
Add to your pubspec.yaml:
dependencies:
flutter_foundation_models: ^0.2.0
dev_dependencies:
flutter_foundation_models_gen: ^0.1.0
build_runner: ^2.4.0
Quick Start
Check Availability
Before using Foundation Models, check if the API is available:
import 'package:flutter_foundation_models/flutter_foundation_models.dart';
if (await SystemLanguageModel.isAvailable) {
// Foundation Models is available, show AI features
} else {
// Not available, hide AI features or show fallback
}
// For detailed availability info:
final availability = await SystemLanguageModel.availability;
if (!availability.isAvailable) {
print('Unavailable: ${availability.unavailableReason}');
}
Basic Text Generation
import 'package:flutter_foundation_models/flutter_foundation_models.dart';
// Check availability first
if (!await SystemLanguageModel.isAvailable) {
print('Foundation Models not available on this device');
return;
}
final session = await LanguageModelSession.create();
final response = await session.respondTo("What is Flutter?");
print(response);
// Don't forget to dispose
session.dispose();
Text Streaming
For real-time text output:
final session = await LanguageModelSession.create();
final stream = session.streamResponseTo("Tell me a joke");
stream.listen((text) {
print(text); // Progressively prints as text is generated
});
Structured Output
Define your data model:
import 'package:flutter_foundation_models/flutter_foundation_models.dart';
part 'movie.g.dart';
@Generable()
class MovieRecommendation {
@Guide(description: "The movie title")
final String title;
@Guide(description: "Release year")
final int year;
@Guide(description: "Brief plot summary")
final String summary;
MovieRecommendation({
required this.title,
required this.year,
required this.summary,
});
}
Run the code generator:
dart run build_runner build
Generate structured content:
final session = await LanguageModelSession.create();
final content = await session.respondToWithSchema(
"Recommend a sci-fi movie from the 1980s",
schema: $MovieRecommendationGenerable.generationSchema,
);
final movie = $MovieRecommendationGenerable.fromGeneratedContent(content);
print('${movie.title} (${movie.year})');
print(movie.summary);
Streaming
For real-time UI updates:
final stream = session.streamResponseToWithSchema(
"Write a short story",
schema: $StoryGenerable.generationSchema,
);
stream.listen((partialContent) {
final partial = $StoryGenerable.fromPartialGeneratedContent(partialContent);
// Update UI with partial.title, partial.content, etc.
// Fields are nullable until fully generated
});
Using Enums
@Generable()
enum Priority { low, medium, high, critical }
@Generable()
class Task {
@Guide(description: "Task description")
final String description;
@Guide(description: "Priority level")
final Priority priority;
Task({required this.description, required this.priority});
}
Generation Guides
Constrain generated values:
@Generable()
class Product {
@Guide(description: "Product name")
final String name;
@Guide(
description: "Price in USD",
guides: [GenerationGuide.range(0.01, 10000)],
)
final double price;
@Guide(
description: "Category",
guides: [GenerationGuide.anyOf(["electronics", "clothing", "food"])],
)
final String category;
@Guide(
description: "Tags for the product",
guides: [GenerationGuide.countRange(1, 5)],
)
final List<String> tags;
Product({
required this.name,
required this.price,
required this.category,
required this.tags,
});
}
Available guides:
| Guide | Description | Applies To |
|---|---|---|
constant(value) |
Exact string value | String |
anyOf(values) |
One of several values | String |
pattern(regex) |
Regex pattern match | String |
minimum(value) |
Minimum value | int, double |
maximum(value) |
Maximum value | int, double |
range(min, max) |
Value range | int, double |
minimumCount(n) |
Min elements | List |
maximumCount(n) |
Max elements | List |
count(n) |
Exact element count | List |
countRange(min, max) |
Element count range | List |
element(guide) |
Apply guide to elements | List |
Tool Use
Let the model call your functions:
// Define tool arguments
@Generable()
class WeatherArgs {
@Guide(description: "City name")
final String city;
WeatherArgs({required this.city});
}
// Define tool result
@Generable()
class WeatherResult {
final String city;
final double temperature;
final String condition;
WeatherResult({
required this.city,
required this.temperature,
required this.condition,
});
}
// Implement the tool
class WeatherTool extends Tool {
@override
String name = "getWeather";
@override
String description = "Get current weather for a city";
@override
GenerationSchema get parameters => $WeatherArgsGenerable.generationSchema;
@override
Future<GeneratedContent> call(GeneratedContent arguments) async {
final args = $WeatherArgsGenerable.fromGeneratedContent(arguments);
// Fetch real weather data here...
return WeatherResult(
city: args.city,
temperature: 72.0,
condition: "sunny",
).toGeneratedContent();
}
}
// Use the tool
final session = await LanguageModelSession.create(
tools: [WeatherTool()],
);
final response = await session.respondTo(
"What's the weather like in San Francisco?",
);
// The model will call WeatherTool and use the result in its response
Generation Options
Fine-tune generation behavior:
final options = GenerationOptions(
sampling: SamplingMode.topP(0.9),
temperature: 0.7,
maximumResponseTokens: 500,
);
final response = await session.respondTo(
"Write a creative story",
options: options,
);
Sampling modes:
SamplingMode.greedy()- Deterministic, always picks most likely tokenSamplingMode.topK(k)- Sample from top K tokensSamplingMode.topP(p)- Sample from tokens with cumulative probability p
System Instructions
Provide context for the model:
final session = await LanguageModelSession.create(
instructions: "You are a helpful cooking assistant. "
"Provide recipes and cooking tips. "
"Always include preparation time and difficulty level.",
);
Model Configuration
Configure the language model with custom settings:
// Use a specific use case
final model = await SystemLanguageModel.create(
useCase: UseCase.contentTagging,
);
// Or with custom guardrails
final model = await SystemLanguageModel.create(
guardrails: Guardrails.permissiveContentTransformations,
);
// Create session with custom model
final session = await LanguageModelSession.create(model: model);
// Don't forget to dispose both
session.dispose();
model.dispose();
Custom Adapters
Load custom adapters for specialized models:
// From a named adapter
final adapter = await Adapter.create(name: "my-adapter");
// Or from a Flutter asset
final adapter = await Adapter.fromAsset("assets/my-adapter.mlmodelc");
final model = await SystemLanguageModel.create(adapter: adapter);
final session = await LanguageModelSession.create(model: model);
// Dispose when done
session.dispose();
model.dispose();
adapter.dispose();
Transcripts
Access and persist conversation history:
final session = await LanguageModelSession.create();
// Have a conversation
await session.respondTo("Hello!");
await session.respondTo("What's 2+2?");
// Get the transcript
final transcript = await session.transcript;
print('Conversation has ${transcript.length} entries');
// Serialize for storage
final json = transcript.toJson();
// Store json to disk, database, etc.
// Later, restore and continue the conversation
final restored = Transcript.fromJson(json);
final newSession = await LanguageModelSession.createWithTranscript(
transcript: restored,
);
await newSession.respondTo("What did we talk about?");
Session Optimization
Reduce latency with prewarming:
final session = await LanguageModelSession.create();
// Prewarm the session before the user starts typing
await session.prewarm();
// Or prewarm with a known prompt prefix
await session.prewarm(promptPrefix: "Translate to Spanish: ");
// Check if session is currently generating
if (await session.isResponding) {
print("Session is busy");
}
API Reference
SystemLanguageModel
| Member | Description |
|---|---|
SystemLanguageModel.isAvailable |
Check if API is available (static) |
SystemLanguageModel.availability |
Get detailed availability info (static) |
SystemLanguageModel.defaultModel |
Access the default model (static) |
SystemLanguageModel.create() |
Create model with configuration (static) |
dispose() |
Release resources |
Adapter
| Member | Description |
|---|---|
Adapter.create(name:) |
Create adapter by name (static) |
Adapter.fromAsset(assetPath) |
Create adapter from Flutter asset (static) |
dispose() |
Release resources |
LanguageModelSession
| Member | Description |
|---|---|
LanguageModelSession.create() |
Create a new session (static) |
respondTo(prompt) |
Generate text response |
streamResponseTo(prompt) |
Stream text response |
respondToWithSchema(prompt, schema:) |
Generate structured content |
streamResponseToWithSchema(prompt, schema:) |
Stream structured content |
prewarm() |
Reduce latency for first request |
isResponding |
Check if session is generating |
dispose() |
Release resources |
Generated Extensions
For a class MyClass annotated with @Generable():
| Member | Description |
|---|---|
$MyClassGenerable.generationSchema |
Schema for generation |
$MyClassGenerable.fromGeneratedContent(content) |
Convert to typed object |
$MyClassGenerable.fromPartialGeneratedContent(content) |
Convert partial (streaming) |
myInstance.toGeneratedContent() |
Convert to GeneratedContent |
$MyClassPartial |
Partial class for streaming |
License
MIT License
Libraries
- flutter_foundation_models
- Flutter Foundation Models - A Flutter plugin for Apple's on-device Foundation Models.