This example demonstrates action-based App Intents using a simple counter application. It shows how to execute specific app functions through Siri voice commands and iOS shortcuts.
This example uses the hybrid approach with:
ios/Runner/AppDelegate.swift)lib/main.dart)| App Interface | iOS Shortcuts |
|---|---|
![]() |
![]() |
| Counter app | iOS Shortcuts app |
cd counter
flutter pub get
flutter run
Manual Testing: Use the floating action button to increment the counter
iOS Shortcuts: Check the Shortcuts app for available actions
Enable Siri: ⚠️ IMPORTANT - In Shortcuts app, tap “Counter Example Shortcuts” and toggle ON the Siri switch (it’s OFF by default)
The iOS side defines static intents in AppDelegate.swift:
struct CounterIntent: AppIntent {
static var title: LocalizedStringResource = "Increment Counter"
static var description = IntentDescription("Increment the counter by one")
static var isDiscoverable = true
func perform() async throws -> some IntentResult & ReturnsValue<String> {
let plugin = FlutterAppIntentsPlugin.shared
let result = await plugin.handleIntentInvocation(
identifier: "increment_counter",
parameters: [:]
)
if let success = result["success"] as? Bool, success {
let value = result["value"] as? String ?? "Counter incremented"
return .result(value: value)
} else {
let errorMessage = result["error"] as? String ?? "Failed to increment counter"
throw AppIntentError.executionFailed(errorMessage)
}
}
}
The Flutter side handles the business logic:
Future<AppIntentResult> _handleIncrementIntent(Map<String, dynamic> parameters) async {
final amount = parameters['amount'] as int? ?? 1;
setState(() => _counter += amount);
// Donate intent for Siri learning
await _client.donateIntent('increment_counter', parameters);
return AppIntentResult.successful(
value: 'Counter incremented by $amount. New value: $_counter',
);
}
The static shortcuts are declared with an AppShortcutsProvider:
struct CounterAppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: CounterIntent(),
phrases: [
"Increment counter with \(.applicationName)",
"Add one with \(.applicationName)",
"Count up using \(.applicationName)"
],
shortTitle: "Increment",
systemImageName: "plus.circle"
)
// ... other shortcuts
}
}
Check out the navigation example to see how App Intents can handle app navigation and deep linking.