{"id":32285959,"url":"https://github.com/cbonello/flutter_app_intents","last_synced_at":"2025-10-23T01:54:04.173Z","repository":{"id":314355054,"uuid":"1050077160","full_name":"cbonello/flutter_app_intents","owner":"cbonello","description":"A Flutter plugin for integrating Apple App Intents with your iOS applications. This plugin enables your Flutter app to work seamlessly with Siri, Shortcuts, Spotlight, and other system experiences on iOS 16.0 and later.","archived":false,"fork":false,"pushed_at":"2025-10-08T01:03:52.000Z","size":4373,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-08T03:09:10.316Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cbonello.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-03T23:14:14.000Z","updated_at":"2025-10-04T01:07:45.000Z","dependencies_parsed_at":"2025-09-12T01:51:20.578Z","dependency_job_id":"5fa3ee80-96e1-44e6-af33-0ad352eace4c","html_url":"https://github.com/cbonello/flutter_app_intents","commit_stats":null,"previous_names":["cbonello/flutter_app_intents"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cbonello/flutter_app_intents","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbonello%2Fflutter_app_intents","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbonello%2Fflutter_app_intents/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbonello%2Fflutter_app_intents/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbonello%2Fflutter_app_intents/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cbonello","download_url":"https://codeload.github.com/cbonello/flutter_app_intents/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbonello%2Fflutter_app_intents/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280546413,"owners_count":26348723,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-22T02:00:06.515Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-23T01:54:03.203Z","updated_at":"2025-10-23T01:54:04.161Z","avatar_url":"https://github.com/cbonello.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flutter App Intents\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/logo.png\" alt=\"Flutter App Intents Logo\" width=\"200\" height=\"200\"\u003e\n\u003c/p\u003e\n\nA Flutter plugin for integrating Apple App Intents with your iOS applications. This plugin enables your Flutter app to work seamlessly with Siri, Shortcuts, Spotlight, and other system experiences on iOS 16.0 and later.\n\n## Features\n\n- **Siri Integration**: Create custom voice commands for your app\n- **Shortcuts Support**: Allow users to create custom shortcuts\n- **Spotlight Integration**: Make your app's actions discoverable in search\n- **Visual Intelligence**: Support for visual search results (iOS 2025+)\n- **Widgets and Controls**: Enhanced widget and control center integration\n- **Type-Safe API**: Strongly typed Dart API with comprehensive error handling\n- **Intent Donation**: Help Siri learn user patterns for improved predictions and suggestions\n\n## Documentation\n\n📖 **[Complete Documentation](https://cbonello.github.io/flutter_app_intents/)** - Visit our comprehensive documentation website with tutorials, examples, and API reference.\n\n## Requirements\n\n- iOS 16.0 or later\n- Flutter 3.8.1 or later\n- Xcode 14.0 or later\n\n## Installation\n\nAdd this to your package's `pubspec.yaml` file:\n\n```yaml\ndependencies:\n  flutter_app_intents: ^0.7.0\n```\n\n### Swift Package Manager (Advanced)\n\nFor iOS developers who want to use the native Swift components directly, this package also supports Swift Package Manager:\n\n```swift\n// In Package.swift\ndependencies: [\n    .package(url: \"https://github.com/cbonello/flutter_app_intents\", from: \"0.7.0\")\n]\n```\n\nOr add via Xcode: **File → Add Package Dependencies** → `https://github.com/cbonello/flutter_app_intents`\n\n\u003e **Note:** SPM support is provided for advanced use cases. Most Flutter developers should use the standard plugin installation above. See [SPM_README.md](SPM_README.md) for detailed SPM integration instructions.\n\n## Architecture Overview\n\nThis plugin uses a **hybrid approach** combining:\n\n1. **Static Swift intents** in your main iOS app target (required for iOS discovery)\n2. **Dynamic Flutter handlers** registered through the plugin (your business logic)\n\n```\niOS Shortcuts/Siri → Static Swift Intent → Flutter Plugin Bridge → Your Flutter Handler\n```\n\nThe static Swift intents act as a bridge, calling your Flutter handlers when executed.\n\n## Simple Example\n\n\u003e **Note:** This example shows the core logic, but a complete implementation requires a corresponding static intent in your iOS app's `AppDelegate.swift`. See the [iOS Configuration](#ios-configuration) section for details.\n\nCreate a voice-controlled counter app in just a few steps:\n\n```dart\n// 1. Register your intent\nfinal client = FlutterAppIntentsClient.instance;\nfinal intent = AppIntentBuilder()\n    .identifier('increment_counter')\n    .title('Increment Counter')\n    .build();\n\nawait client.registerIntent(intent, (parameters) async {\n  // Your business logic here\n  incrementCounter();\n  return AppIntentResult.successful(value: 'Counter incremented!');\n});\n```\n\n```swift\n// 2. Add static intent to iOS (AppDelegate.swift)\nimport AppIntents\n\nstruct IncrementCounterIntent: AppIntent {\n    static var title: LocalizedStringResource = \"Increment Counter\"\n\n    func perform() async throws -\u003e some IntentResult {\n        await FlutterAppIntentsPlugin.shared.handleIntent(\"increment_counter\", [:])\n        return .result()\n    }\n}\n```\n\n**Result:** Say *\"Hey Siri, increment counter\"* and your Flutter function runs! 🎉\n\n## Quick Start\n\n\u003e 📖 **New to App Intents?** Check out our [Step-by-Step Tutorial](documentation/TUTORIAL.md) for a complete walkthrough from `flutter create` to working Siri integration!\n\n### 1. Import the package\n\n```dart\nimport 'package:flutter_app_intents/flutter_app_intents.dart';\n```\n\n### 2. Add static intents to iOS (Required)\n\n⚠️ **First, add static App Intents to your iOS `AppDelegate.swift`** (see iOS Configuration section below)\n\n### 3. Create and register Flutter handlers\n\n```dart\nclass MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    // Register intents during app initialization\n    _setupAppIntents();\n    \n    return MaterialApp(\n      title: 'My App',\n      home: MyHomePage(),\n    );\n  }\n  \n  Future\u003cvoid\u003e _setupAppIntents() async {\n    final client = FlutterAppIntentsClient.instance;\n    \n    // Create an intent using the builder\n    final incrementIntent = AppIntentBuilder()\n        .identifier('increment_counter')\n        .title('Increment Counter')\n        .description('Increments the counter by one')\n        .parameter(const AppIntentParameter(\n          name: 'amount',\n          title: 'Amount',\n          type: AppIntentParameterType.integer,\n          isOptional: true,\n          defaultValue: 1,\n        ))\n        .build();\n    \n    // Register with a handler\n    await client.registerIntent(incrementIntent, (parameters) async {\n      final amount = parameters['amount'] as int? ?? 1;\n      \n      // Your business logic here\n      incrementCounter(amount);\n      \n      return AppIntentResult.successful(\n        value: 'Counter incremented by $amount',\n      );\n    });\n  }\n}\n```\n\n### 3. Handle intent execution\n\n```dart\nFuture\u003cAppIntentResult\u003e handleIncrementIntent(Map\u003cString, dynamic\u003e parameters) async {\n  try {\n    final amount = parameters['amount'] as int? ?? 1;\n    \n    // Perform your app's logic\n    final newValue = incrementCounter(amount);\n    \n    // Donate the intent to help Siri learn\n    await FlutterAppIntentsClient.instance.donateIntent(\n      'increment_counter',\n      parameters,\n    );\n    \n    return AppIntentResult.successful(\n      value: 'Counter is now $newValue',\n    );\n  } catch (e) {\n    return AppIntentResult.failed(\n      error: 'Failed to increment counter: $e',\n    );\n  }\n}\n```\n\n## Navigation with App Intents\n\nOur plugin excels at handling app navigation through voice commands and shortcuts. Here's how to implement navigation intents:\n\n### Navigation Intent Pattern\n\nFor navigation, use `needsToContinueInApp: true` to tell iOS to focus your app and `OpensIntent` return type in Swift:\n\n**iOS Implementation:**\n```swift\n@available(iOS 16.0, *)\nstruct OpenProfileIntent: AppIntent {\n    static var title: LocalizedStringResource = \"Open Profile\"\n    static var description = IntentDescription(\"Open user profile page\")\n    static var isDiscoverable = true\n    static var openAppWhenRun = true\n    \n    @Parameter(title: \"User ID\")\n    var userId: String?\n    \n    func perform() async throws -\u003e some IntentResult \u0026 ReturnsValue\u003cString\u003e \u0026 OpensIntent {\n        let plugin = FlutterAppIntentsPlugin.shared\n        let result = await plugin.handleIntentInvocation(\n            identifier: \"open_profile\",\n            parameters: [\"userId\": userId ?? \"current\"]\n        )\n        \n        if let success = result[\"success\"] as? Bool, success {\n            let value = result[\"value\"] as? String ?? \"Profile opened\"\n            return .result(value: value) // This opens/focuses the app\n        } else {\n            let errorMessage = result[\"error\"] as? String ?? \"Failed to open profile\"\n            throw AppIntentError.executionFailed(errorMessage)\n        }\n    }\n}\n```\n\n**Flutter Handler:**\n```dart\nFuture\u003cAppIntentResult\u003e _handleOpenProfileIntent(\n  Map\u003cString, dynamic\u003e parameters,\n) async {\n  final userId = parameters['userId'] as String? ?? 'current';\n  \n  // Navigate to the target page\n  Navigator.of(context).pushNamed('/profile', arguments: {'userId': userId});\n  \n  return AppIntentResult.successful(\n    value: 'Opening profile for user $userId',\n    needsToContinueInApp: true, // Critical: focuses the app\n  );\n}\n```\n\n### Common Navigation Patterns\n\n#### 1. Deep Linking with Parameters\n```dart\n// Navigate to specific content with parameters\nFuture\u003cAppIntentResult\u003e _handleOpenChatIntent(Map\u003cString, dynamic\u003e parameters) async {\n  final contactName = parameters['contactName'] as String;\n  \n  Navigator.of(context).pushNamed('/chat', arguments: {\n    'contactName': contactName,\n    'openedViaIntent': true,\n  });\n  \n  return AppIntentResult.successful(\n    value: 'Opening chat with $contactName',\n    needsToContinueInApp: true,\n  );\n}\n```\n\n#### 2. Search Navigation\n```dart\n// Handle search queries with navigation\nFuture\u003cAppIntentResult\u003e _handleSearchIntent(Map\u003cString, dynamic\u003e parameters) async {\n  final query = parameters['query'] as String;\n  \n  Navigator.of(context).pushNamed('/search', arguments: {'query': query});\n  \n  return AppIntentResult.successful(\n    value: 'Searching for \"$query\"',\n    needsToContinueInApp: true,\n  );\n}\n```\n\n#### 3. Settings/Configuration Navigation\n```dart\n// Navigate to specific settings pages\nFuture\u003cAppIntentResult\u003e _handleOpenSettingsIntent(Map\u003cString, dynamic\u003e parameters) async {\n  final section = parameters['section'] as String? ?? 'general';\n  \n  Navigator.of(context).pushNamed('/settings/$section');\n  \n  return AppIntentResult.successful(\n    value: 'Opening $section settings',\n    needsToContinueInApp: true,\n  );\n}\n```\n\n### Navigation with GoRouter\n\nIf you're using GoRouter, the pattern is similar:\n\n```dart\nFuture\u003cAppIntentResult\u003e _handleNavigationIntent(Map\u003cString, dynamic\u003e parameters) async {\n  final route = parameters['route'] as String;\n  \n  // Use GoRouter for navigation\n  context.go(route);\n  \n  return AppIntentResult.successful(\n    value: 'Navigating to $route',\n    needsToContinueInApp: true,\n  );\n}\n```\n\n### AppShortcuts for Navigation\n\nAdd navigation shortcuts to your `AppShortcutsProvider`:\n\n```swift\n@available(iOS 16.0, *)\nstruct AppShortcuts: AppShortcutsProvider {\n    static var appShortcuts: [AppShortcut] {\n        return [\n            // Navigation shortcuts\n            AppShortcut(\n                intent: OpenProfileIntent(),\n                phrases: [\n                    \"Open my profile in ${applicationName}\",\n                    \"Show profile using ${applicationName}\",\n                    \"Go to profile with ${applicationName}\"\n                ]\n            ),\n            AppShortcut(\n                intent: OpenChatIntent(),\n                phrases: [\n                    \"Chat with \\\\(.contactName) using ${applicationName}\",\n                    \"Open chat with \\\\(.contactName) in ${applicationName}\",\n                    \"Message \\\\(.contactName) with ${applicationName}\"\n                ]\n            )\n        ]\n    }\n}\n```\n\n### Navigation vs Action Intents\n\n| Intent Type | Return Type | Use Case | Example |\n|-------------|-------------|----------|---------|\n| **Query** | `ReturnsValue\u003cString\u003e` | Get information only | \"Get counter value\", \"Check weather\" |\n| **Action + App Opening** | `ReturnsValue\u003cString\u003e \u0026 OpensIntent` | Execute + show result | \"Increment counter\", \"Send message\" |\n| **Navigation** | `ReturnsValue\u003cString\u003e \u0026 OpensIntent` | Navigate to pages | \"Open profile\", \"Show chat\" |\n\n## API Reference\n\n### Flutter App IntentsClient\n\nThe main client class for managing App Intents:\n\n#### Methods\n\n- `registerIntent(AppIntent intent, handler)` - Register a single intent with handler\n- `registerIntents(Map\u003cAppIntent, handler\u003e)` - Register multiple intents\n- `unregisterIntent(String identifier)` - Remove an intent\n- `getRegisteredIntents()` - Get all registered intents\n- `updateShortcuts()` - Refresh app shortcuts\n- `donateIntent(String identifier, parameters)` - Intent donation for Siri learning and predictions\n\n### AppIntent\n\nRepresents an App Intent configuration:\n\n```dart\nconst AppIntent({\n  required String identifier,      // Unique ID\n  required String title,          // Display name\n  required String description,    // What it does\n  List\u003cAppIntentParameter\u003e parameters = const [],\n  bool isEligibleForSearch = true,\n  bool isEligibleForPrediction = true,\n  AuthenticationPolicy authenticationPolicy = AuthenticationPolicy.none,\n});\n```\n\n### AppIntentParameter\n\nDefines parameters that can be passed to intents:\n\n```dart\nconst AppIntentParameter({\n  required String name,           // Parameter name\n  required String title,          // Display title\n  required AppIntentParameterType type,\n  String? description,\n  bool isOptional = false,\n  dynamic defaultValue,\n});\n```\n\n### AppIntentResult\n\nResult returned from intent execution:\n\n```dart\n// Successful result\nAppIntentResult.successful(\n  value: 'Operation completed',\n  needsToContinueInApp: false,\n);\n\n// Failed result\nAppIntentResult.failed(\n  error: 'Something went wrong',\n);\n```\n\n### AppIntentBuilder\n\nFluent API for creating intents:\n\n```dart\nfinal intent = AppIntentBuilder()\n    .identifier('my_intent')\n    .title('My Intent')\n    .description('Does something useful')\n    .parameter(myParameter)\n    .eligibleForSearch(true)\n    .authenticationPolicy(AuthenticationPolicy.requiresAuthentication)\n    .build();\n```\n\n## Enhanced Intent Donation\n\nThe plugin provides advanced intent donation capabilities to help Siri learn user patterns and provide better predictions.\n\n### Basic Intent Donation\n\n```dart\n// Donate intent for Siri learning\nawait FlutterAppIntentsClient.instance.donateIntent(\n  'my_intent',\n  {'param': 'value'},\n);\n```\n\n## Donation Best Practices\n\nDonate intents after successful execution to help Siri learn user patterns:\n\n```dart\n// Execute the intent action\nfinal result = await performAction();\n\n// Donate if successful\nif (result.isSuccess) {\n  await FlutterAppIntentsClient.instance.donateIntent(\n    'my_intent',\n    parameters,\n  );\n}\n```\n\n**When to donate:**\n- After user successfully completes an action\n- When an intent is invoked via Siri or Shortcuts\n- For frequently-used features to improve predictions\n\n**When not to donate:**\n- After failed operations\n- For background/automated tasks\n- For one-time setup actions\n\n## iOS Configuration\n\n### Required Setup: Static App Intents in Main App Target\n\n**⚠️ Important**: iOS App Intents framework requires static intent declarations in your main app target, not just dynamic registration from the plugin. \n\nAdd this code to your iOS app's `AppDelegate.swift`:\n\n```swift\nimport Flutter\nimport UIKit\nimport AppIntents\nimport flutter_app_intents\n\n@main\n@objc class AppDelegate: FlutterAppDelegate {\n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -\u003e Bool {\n    GeneratedPluginRegistrant.register(with: self)\n    return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n  }\n}\n\n// Static App Intents that bridge to Flutter handlers\n@available(iOS 16.0, *)\nstruct MyCounterIntent: AppIntent {\n    static var title: LocalizedStringResource = \"Increment Counter\"\n    static var description = IntentDescription(\"Increment the counter by one\")\n    static var isDiscoverable = true\n    static var openAppWhenRun = true\n    \n    func perform() async throws -\u003e some IntentResult \u0026 ReturnsValue\u003cString\u003e \u0026 OpensIntent {\n        let plugin = FlutterAppIntentsPlugin.shared\n        let result = await plugin.handleIntentInvocation(\n            identifier: \"increment_counter\", \n            parameters: [:]\n        )\n        \n        if let success = result[\"success\"] as? Bool, success {\n            let value = result[\"value\"] as? String ?? \"Counter incremented\"\n            return .result(value: value)\n        } else {\n            let errorMessage = result[\"error\"] as? String ?? \"Failed to increment counter\"\n            throw AppIntentError.executionFailed(errorMessage)\n        }\n    }\n}\n\n// Error handling for App Intents\nenum AppIntentError: Error {\n    case executionFailed(String)\n}\n\n// AppShortcutsProvider for Siri/Shortcuts discovery\n@available(iOS 16.0, *)\nstruct AppShortcuts: AppShortcutsProvider {\n    static var appShortcuts: [AppShortcut] {\n        return [\n            AppShortcut(\n                intent: MyCounterIntent(),\n                phrases: [\n                    \"Increment counter with \\(.applicationName)\",\n                    \"Add one with \\(.applicationName)\",\n                    \"Count up using \\(.applicationName)\"\n                ]\n            )\n        ]\n    }\n}\n```\n\n### App Shortcuts Phrase Best Practices\n\nWhen defining phrases for your App Shortcuts, follow these best practices for optimal user experience and Siri recognition:\n\n#### ⚠️ Important Limitation: Static Phrases Only\n\n**App Shortcuts phrases CANNOT be defined dynamically.** This is a fundamental limitation of Apple's App Intents framework:\n\n```swift\n// ❌ DOES NOT WORK - phrases must be static literals\nphrases: [\n    \"\\(userDefinedPhrase) with \\(.applicationName)\",     // Won't compile\n    dynamicPhraseVariable,                               // Won't compile\n    generatePhrase()                                     // Won't compile\n]\n\n// ✅ WORKS - static phrases with dynamic parameters\nphrases: [\n    \"Send message to \\(.contactName) with \\(.applicationName)\",    // ✅ Parameter is dynamic\n    \"Set timer for \\(.duration) using \\(.applicationName)\",       // ✅ Parameter is dynamic\n    \"Play \\(.songName) in \\(.applicationName)\"                    // ✅ Parameter is dynamic\n]\n```\n\n**Why phrases must be static:**\n- **Compile-time registration**: iOS requires phrases for Siri's speech recognition engine at build time\n- **App Store review**: Apple analyzes all possible voice commands during app review\n- **Performance**: Siri's recognition is optimized based on the known phrase list\n- **Security**: Prevents apps from creating potentially malicious or conflicting commands dynamically\n\n**Workarounds for dynamic content:**\n1. **Use parameters** for the dynamic parts (user names, amounts, etc.)\n2. **Provide comprehensive variations** to cover common use cases\n3. **Create multiple intent types** for different scenarios instead of one dynamic intent\n\n#### 📊 Phrase Quantity Limits and Guidelines\n\nWhile Apple doesn't publish exact hard limits, there are practical constraints on the number of phrases:\n\n**Recommended Limits:**\n- **Per AppShortcut**: 3-5 phrases (optimal), up to 8 phrases (maximum recommended)\n- **Total per app**: 50-100 phrases across all shortcuts (practical limit)\n- **Quality over quantity**: Focus on natural, distinct variations rather than exhaustive lists\n\n```swift\n// ✅ GOOD - Focused, natural variations (4 phrases)\nAppShortcut(\n    intent: SendMessageIntent(),\n    phrases: [\n        \"Send message to \\(.contactName) with \\(.applicationName)\",\n        \"Text \\(.contactName) using \\(.applicationName)\",\n        \"Message \\(.contactName) in \\(.applicationName)\",\n        \"Write to \\(.contactName) with \\(.applicationName)\"\n    ]\n)\n\n// ❌ EXCESSIVE - Too many similar phrases (impacts performance)\nAppShortcut(\n    intent: SendMessageIntent(),\n    phrases: [\n        \"Send message to \\(.contactName) with \\(.applicationName)\",\n        \"Send a message to \\(.contactName) with \\(.applicationName)\",\n        \"Send text message to \\(.contactName) with \\(.applicationName)\",\n        \"Send a text message to \\(.contactName) with \\(.applicationName)\",\n        // ... 15+ more variations\n    ]\n)\n```\n\n**Performance Impact:**\n- **More phrases = longer Siri processing time**\n- **Diminishing returns**: Beyond 5-8 phrases, recognition accuracy may decrease\n- **Memory usage**: Each phrase consumes system resources\n- **User confusion**: Too many options can overwhelm users\n\n**Best Strategy:**\n1. **Start with 3-4 core phrases** that feel most natural\n2. **Test with real users** to see which phrases they actually use\n3. **Add variations based on user feedback** rather than guessing\n4. **Remove unused phrases** to optimize performance\n\n#### 1. Include App Name for Disambiguation\n**✅ Recommended:**\n```swift\nphrases: [\n    \"Increment counter with \\(.applicationName)\",\n    \"Add one using \\(.applicationName)\",\n    \"Count up in \\(.applicationName)\"\n]\n```\n\n**❌ Avoid:**\n```swift\nphrases: [\n    \"Increment counter\",  // Too generic, conflicts with other apps\n    \"Add one\"             // Ambiguous without context\n]\n```\n\n#### 2. Use Natural Prepositions\nChoose prepositions that sound natural in conversation:\n- **\"with \\(.applicationName)\"** - Most common, works for actions\n- **\"using \\(.applicationName)\"** - Good for tool-like actions  \n- **\"in \\(.applicationName)\"** - Natural for location-based commands\n- **\"from \\(.applicationName)\"** - Perfect for queries and data retrieval\n\n#### 3. Provide Multiple Variations\nOffer 3-5 phrase variations to accommodate different user preferences:\n```swift\nphrases: [\n    \"Increment counter with \\(.applicationName)\",      // Formal\n    \"Add one using \\(.applicationName)\",               // Casual\n    \"Count up in \\(.applicationName)\",                 // Alternative verb\n    \"Bump counter with \\(.applicationName)\",           // Colloquial\n    \"Increase count using \\(.applicationName)\"         // Descriptive\n]\n```\n\n#### 4. Keep Phrases Concise but Descriptive\n- **Ideal length**: 3-6 words (excluding app name)\n- **Be specific**: \"Increment counter\" vs. \"Do something\"\n- **Avoid filler words**: Skip \"please\", \"can you\", \"I want to\"\n\n#### 5. Alternative Patterns\n\n**App Name at Beginning** (less common but valid):\n```swift\nphrases: [\n    \"Use \\(.applicationName) to increment counter\",\n    \"Tell \\(.applicationName) to reset timer\"\n]\n```\n\n**Action-First Pattern** (most natural):\n```swift\nphrases: [\n    \"Start workout with \\(.applicationName)\",\n    \"Send message using \\(.applicationName)\",\n    \"Check weather in \\(.applicationName)\"\n]\n```\n\n#### 6. Testing Your Phrases\n- **Test with Siri**: Speak each phrase to ensure recognition\n- **Try variations**: Users might not say exactly what you expect\n- **Check conflicts**: Ensure phrases don't overlap with system commands\n- **User feedback**: Monitor which phrases users actually use\n\n#### 7. Common Phrase Patterns by Intent Type\n\n**Action Intents:**\n```swift\n\"[Action] [Object] with \\(.applicationName)\"\n\"[Verb] [Noun] using \\(.applicationName)\"\n```\n\n**Query Intents:**\n```swift\n\"Get [Data] from \\(.applicationName)\"\n\"Check [Status] in \\(.applicationName)\"\n\"What's [Information] using \\(.applicationName)\"\n```\n\n**Navigation Intents:**\n```swift\n\"Open [Page] in \\(.applicationName)\"\n\"Go to [Section] using \\(.applicationName)\"\n\"Show [Content] with \\(.applicationName)\"\n```\n\n### Info.plist Configuration\n\nAdd these permissions and configuration to your iOS `Info.plist`:\n\n```xml\n\u003ckey\u003eNSMicrophoneUsageDescription\u003c/key\u003e\n\u003cstring\u003eThis app uses microphone for Siri integration\u003c/string\u003e\n\u003ckey\u003eNSSpeechRecognitionUsageDescription\u003c/key\u003e\n\u003cstring\u003eThis app uses speech recognition for Siri integration\u003c/string\u003e\n\n\u003c!-- App Intents Configuration --\u003e\n\u003ckey\u003eNSAppIntentsConfiguration\u003c/key\u003e\n\u003cdict\u003e\n    \u003ckey\u003eNSAppIntentsPackage\u003c/key\u003e\n    \u003cstring\u003eyour_app_bundle_id\u003c/string\u003e\n\u003c/dict\u003e\n\u003ckey\u003eNSAppIntentsMetadata\u003c/key\u003e\n\u003cdict\u003e\n    \u003ckey\u003eNSAppIntentsSupported\u003c/key\u003e\n    \u003ctrue/\u003e\n\u003c/dict\u003e\n```\n\n### Minimum Deployment Target\n\nEnsure your iOS deployment target is set to 16.0 or later:\n\n```ruby\n# ios/Podfile\nplatform :ios, '16.0'\n```\n\n## Parameter Types\n\nThe following parameter types are supported:\n\n- `AppIntentParameterType.string` - Text input\n- `AppIntentParameterType.integer` - Whole numbers\n- `AppIntentParameterType.boolean` - True/false values\n- `AppIntentParameterType.double` - Decimal numbers\n- `AppIntentParameterType.date` - Date/time values\n- `AppIntentParameterType.url` - Web URLs\n- `AppIntentParameterType.file` - File references\n- `AppIntentParameterType.entity` - Custom app-specific types\n\n## Authentication Policies\n\nControl when intents can be executed:\n\n- `AuthenticationPolicy.none` - No authentication required\n- `AuthenticationPolicy.requiresAuthentication` - User must be authenticated\n- `AuthenticationPolicy.requiresUnlockedDevice` - Device must be unlocked\n\n## Best Practices\n\n### General Practices\n1. **Keep intent names simple and descriptive**\n2. **Use appropriate parameter types**\n3. **Provide good descriptions for discoverability**\n4. **Handle errors gracefully**\n5. **Test with Siri and Shortcuts app**\n\n### Long-Running Operations and Loading States\n6. **No loading indicators during intent execution** - App Intents run outside your Flutter app's UI context through iOS's system-level framework, so you cannot display loading indicators during execution\n7. **Use `needsToContinueInApp: true` for long operations** - Return immediately and continue processing in your app:\n   ```dart\n   Future\u003cAppIntentResult\u003e _handleLongOperation(Map\u003cString, dynamic\u003e parameters) async {\n     // Start background work but return immediately\n     _startBackgroundWork();\n     \n     return AppIntentResult.successful(\n       value: 'Operation started, opening app for progress...',\n       needsToContinueInApp: true,  // Opens your app where you can show progress\n     );\n   }\n   ```\n8. **Show progress in Flutter app after intent redirect** - Display loading indicators in your Flutter UI after the intent opens your app\n9. **Consider timeout handling** - Long-running intents may timeout at the system level, so break work into smaller chunks\n\n### App Opening Behavior\n10. **Use `static var openAppWhenRun = true`** in Swift intents that should open the app\n11. **Add `\u0026 OpensIntent`** to the return type for intents that open the app\n12. **Include `needsToContinueInApp: true`** in Flutter results for visual feedback\n13. **Choose appropriate behavior**: Some intents (like queries) may not need to open the app\n\n### Navigation Intents\n14. **Always use `needsToContinueInApp: true`** for navigation intents\n15. **Add `static var openAppWhenRun = true`** to force app opening\n16. **Use `ReturnsValue\u003cString\u003e \u0026 OpensIntent`** return type in Swift\n17. **Handle app state properly** - check if context is still mounted\n18. **Pass meaningful parameters** to destination pages\n19. **Consider app lifecycle** - navigation may happen when app is backgrounded\n\n### Intent Donation Strategy\n20. **Donate intents strategically**:\n   - Use enhanced donation with metadata for better Siri learning\n   - Donate after successful execution only\n   - Use appropriate relevance scores based on usage patterns\n   - Provide contextual information to improve predictions\n   - Use batch donations for related intents\n21. **Navigation intents should have high relevance** (0.8-1.0) when user-initiated\n22. **Monitor donation performance and adjust relevance scores** based on user behavior\n\n### App Integration\n23. **Static intents must match Flutter handlers** - ensure identifier consistency\n24. **Handle app cold starts** - navigation intents may launch your app\n25. **Test edge cases** - what happens when target pages don't exist?\n26. **Provide fallback navigation** - graceful handling of invalid routes\n\n## Examples\n\n### 📚 Tutorial: Simple Counter App\nOur [Step-by-Step Tutorial](documentation/TUTORIAL.md) walks you through building a complete counter app with Siri integration from scratch.\n\n### 🔍 Example Apps\nCheck out the [example apps](example/) for complete implementations showing different App Intent patterns:\n\n#### 1. [Counter Example](example/counter/) - Action Intents\n- Counter increment/reset/query intents\n- Parameter handling with type safety\n- Error management and validation\n- Action-based voice commands\n\n#### 2. [Navigation Example](example/navigation/) - Navigation Intents  \n- Deep linking with parameters\n- Search navigation patterns\n- Settings page navigation\n- App focusing and lifecycle management\n- Multi-page Flutter navigation\n\n#### 3. [Weather Example](example/weather/) - Query Intents\n- Background data queries with voice responses\n- Weather information retrieval\n- Temperature and forecast queries\n- Boolean rain checks without opening app\n- `ProvidesDialog` for Siri speech output\n\n### Advanced Features\n- Basic and enhanced intent donation\n- Batch donation examples\n- Relevance score optimization\n- Context-aware donations\n- Siri integration testing\n- Navigation with Flutter Router and GoRouter\n\n## Troubleshooting\n\n### \"App Intents are only supported on iOS\"\n\nThis plugin only works on iOS 16.0+. Make sure you're testing on a compatible device or simulator.\n\n### Intents not appearing in Siri/Shortcuts\n\n**Most Common Issues**: Missing static App Intents or disabled Siri integration\n\n1. **Verify static intents are declared** in your `AppDelegate.swift` (see iOS Configuration above)\n2. **Ensure AppShortcutsProvider exists** in your main app target\n3. **Enable Siri for App Shortcuts**: In iOS Shortcuts app → [Your App] Shortcuts → Toggle ON the Siri switch (it's OFF by default)\n4. **Check intent identifiers match** between static Swift intents and Flutter handlers\n5. **Restart the app completely** after adding static intents\n6. Ensure intents are registered successfully on Flutter side\n7. Check that `isEligibleForPrediction` is `true`\n8. Try donating the intent after manual execution\n9. Restart the Shortcuts app\n\n**Architecture Note**: iOS App Intents framework requires static intent declarations at compile time for Siri/Shortcuts discovery. Dynamic registration from Flutter plugins alone is not sufficient.\n\n### Voice commands not recognized\n\n1. Use simple, clear command phrases\n2. Test different phrasings\n3. Check Siri's language settings\n4. Verify intent titles are descriptive\n\n### Navigation intents not working\n\n1. **Verify `needsToContinueInApp: true`** in Flutter result\n2. **Check `OpensIntent` return type** in Swift intent\n3. **Ensure routes exist** in your app's navigation setup\n4. **Test app lifecycle** - try when app is backgrounded vs foreground\n5. **Check mounted context** before navigation calls\n6. **Verify parameter passing** to destination screens\n\n### Intent donations not improving predictions\n\n1. **Ensure proper relevance scores**: Use higher scores (0.8-1.0) for frequently used actions\n2. **Provide meaningful context**: Include feature names, user actions, and usage patterns\n3. **Donate consistently**: Only donate after successful intent execution\n4. **Use batch donations**: Group related intents for better learning\n5. **Monitor and adjust**: Regularly review and update relevance scores based on usage analytics\n\n### Intent Donation Errors\n\nIf intent donation fails, check:\n- The intent identifier matches a registered intent\n- Parameters match the intent's defined parameters\n- The intent was successfully registered before donation\n\n## Apple Documentation References\n\nFor deeper understanding of the underlying iOS concepts, refer to these official Apple resources:\n\n### Core App Intents Framework\n- **[App Intents Framework](https://developer.apple.com/documentation/appintents)** - Complete framework documentation\n- **[App Intent Protocol](https://developer.apple.com/documentation/appintents/appintent)** - Core protocol documentation\n- **[Intent Result](https://developer.apple.com/documentation/appintents/intentresult)** - Understanding intent return values\n\n### Siri Integration\n- **[Making App Intents Available to Siri](https://developer.apple.com/documentation/appintents/making-app-intents-available-to-siri)** - Core Siri integration guide\n- **[App Shortcuts](https://developer.apple.com/documentation/appintents/appshortcut)** - AppShortcut protocol documentation\n- **[App Shortcuts Provider](https://developer.apple.com/documentation/appintents/appshortcutsprovider)** - Managing app shortcuts\n- **[Shortcuts App Integration](https://developer.apple.com/documentation/appintents/making-your-app-available-with-app-intents)** - Shortcuts app integration\n\n### Parameters and Data Types\n- **[App Intent Parameter](https://developer.apple.com/documentation/appintents/appintent/parameter/)** - Parameter protocol documentation\n- **[Intent Parameter](https://developer.apple.com/documentation/appintents/intentparameter)** - Intent parameter wrapper\n- **[App Entity](https://developer.apple.com/documentation/appintents/appentity)** - Custom entity parameters\n- **[Parameter Summary](https://developer.apple.com/documentation/appintents/parametersummary)** - Parameter display configuration\n\n### Intent Donation and Learning\n- **[Making App Intents Available to Siri](https://developer.apple.com/documentation/appintents/making-app-intents-available-to-siri)** - Core donation and prediction guide\n- **[App Intents and User Activity](https://developer.apple.com/documentation/appintents/making-your-app-available-with-app-intents)** - Integration patterns\n- **[Siri Tips and Suggestions](https://developer.apple.com/documentation/sirikit/donating_shortcuts_to_siri)** - Improving suggestions and learning\n\n### Navigation and App Opening\n- **[Opening Your App](https://developer.apple.com/documentation/appintents/making-app-intents-available-to-siri#Open-your-app-through-an-app-intent)** - App opening patterns\n- **[Opens Intent Protocol](https://developer.apple.com/documentation/appintents/opensintent)** - Protocol for opening apps\n- **[App Intent Execution](https://developer.apple.com/documentation/appintents/making-your-app-available-with-app-intents)** - Managing app state during intent execution\n\n### Authentication and Security\n- **[Intent Authentication](https://developer.apple.com/documentation/appintents/making-app-intents-available-to-siri#Require-authentication-for-an-app-intent)** - Securing your intents\n- **[Authentication Policy](https://developer.apple.com/documentation/appintents/intentauthenticationpolicy)** - Authentication policy options\n- **[App Intents Privacy](https://developer.apple.com/documentation/appintents/making-your-app-available-with-app-intents#Privacy-considerations)** - Privacy best practices\n\n### Advanced Topics\n- **[Interactive Widgets](https://developer.apple.com/documentation/widgetkit/making-a-configurable-widget)** - Widget integration with App Intents\n- **[App Extensions](https://developer.apple.com/documentation/appintents/making-your-app-available-with-app-intents#App-extensions)** - Extension-based intents\n- **[Intent Phrases](https://developer.apple.com/documentation/appintents/making-app-intents-available-to-siri#Use-phrases-to-customize-what-users-can-say)** - Custom phrases and recognition\n- **[App Intents Testing](https://developer.apple.com/documentation/appintents/making-your-app-available-with-app-intents#Testing-your-app-intent)** - Testing and debugging\n\n### WWDC Sessions\n- **[WWDC 2022: Dive into App Intents](https://developer.apple.com/videos/play/wwdc2022/10032/)** - Introduction to App Intents\n- **[WWDC 2022: Implement App Shortcuts with App Intents](https://developer.apple.com/videos/play/wwdc2022/10170/)** - Practical implementation\n- **[WWDC 2023: Explore enhancements to App Intents](https://developer.apple.com/videos/play/wwdc2023/10103/)** - Latest features and improvements\n\n### Design Guidelines\n- **[Human Interface Guidelines: Siri](https://developer.apple.com/design/human-interface-guidelines/technologies/siri/)** - Designing for Siri interactions\n- **[App Shortcuts Guidelines](https://developer.apple.com/design/human-interface-guidelines/app-shortcuts)** - User experience patterns for shortcuts\n- **[Accessibility in Siri](https://developer.apple.com/design/human-interface-guidelines/accessibility)** - Inclusive voice interface design\n\n## Contributing\n\nThis package is an independent Flutter plugin for Apple App Intents integration.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcbonello%2Fflutter_app_intents","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcbonello%2Fflutter_app_intents","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcbonello%2Fflutter_app_intents/lists"}