Docs
grovs.io Start for free

Quick start

Install and configure the Grovs Flutter SDK in your app.

Install the plugin, add the native configuration and open your first link on a device. Takes about fifteen minutes.

Using an AI coding agent?

Copy one prompt into Claude Code, Cursor or Copilot and it does these steps in your project.

Open in Cursor

Upgrading from 3.0? See the 3.1 upgrade guide. From 1.x, start with the 3.0 upgrade guide.

Requirements

  • Flutter 3.38.0+, Dart 3.9.2+
  • iOS 13.0+
  • Android 7.0 (API 24)+. The launcher activity can extend FlutterActivity (the default) or FlutterFragmentActivity.
  • Your bundle ID, package name and SHA-256 signing fingerprint registered in the Grovs dashboard. See How to Get the Package Name and How to Get the SHA-256 Fingerprint

Installation

Add to pubspec.yaml:

YAML
dependencies:
  grovs_flutter_plugin: ^3.1.1
Bash
flutter pub get

The plugin pulls in the native SDKs (Grovs from 3.1.0 on iOS, io.grovs:Grovs:3.1.0 on Android). Don't add them to your app separately. On iOS it builds with Swift Package Manager when Flutter's SPM support is on, and with CocoaPods (Grovs ~> 3.1) otherwise.

Configuration

There's no configure call in Dart. The plugin reads native config files and configures the SDK at launch, before any Dart runs.

Info.plist keyAndroidManifest.xml meta-data nameDefaultDescription
GrovsApiKey (String)grovs_api_key (string)requiredProject API key from the Grovs dashboard. If missing, iOS doesn't configure the SDK at all; Android passes an empty string.
GrovsUseTestEnvironment (Boolean)grovs_use_test_environment (boolean)falsetrue targets the test environment, false production. Match your API key's environment.
GrovsBaseURL (String)grovs_base_url (string)Grovs cloudDomain of a self-hosted backend, for example https://sdk.example.com.
GrovsClipboardDomains (Array of String)grovs_clipboard_domains (comma-separated string)noneExtra link hosts accepted by clipboard-assisted deferred deep linking. Grovs-owned hosts are always accepted.
GrovsEnabled (Boolean)grovs_enabled (boolean)trueWhether the SDK may collect and send anything this launch. Set false and call setSDK(true) after consent. See Privacy & Consent.

The plugin always turns native automatic screen tracking off. Screen views come from Grovs.navigatorObserver; see Events & Screen Tracking.

Android setup

1. Add configuration to AndroidManifest.xml

Inside <application> in android/app/src/main/AndroidManifest.xml:

XML
<application>
    <meta-data
        android:name="grovs_api_key"
        android:value="YOUR_API_KEY" />
    <meta-data
        android:name="grovs_use_test_environment"
        android:value="true" />
 
    <!-- Optional: custom base URL for self-hosted backends -->
    <meta-data
        android:name="grovs_base_url"
        android:value="https://sdk.example.com" />
 
    <!-- Optional: start disabled until the user gives consent (default true) -->
    <meta-data
        android:name="grovs_enabled"
        android:value="false" />
 
    <!-- Optional: extra hosts accepted for clipboard deferred deep linking -->
    <meta-data
        android:name="grovs_clipboard_domains"
        android:value="links.example.com,promo.example.com" />
</application>

The plugin configures the SDK once per process when the first Flutter engine attaches. It follows the host activity's own lifecycle to call Grovs.onStart, and forwards onNewIntent from the engine's activity. MainActivity needs no changes.

2. Add intent filters

Add these to your launcher activity:

XML
<activity android:name=".MainActivity">
    <!-- Custom URL scheme -->
    <intent-filter>
        <data android:scheme="your_app_scheme" android:host="open" />
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
    </intent-filter>
 
    <!-- App links (production) -->
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" android:host="your_app_host" />
    </intent-filter>
 
    <!-- App links (test) -->
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" android:host="your_app_test_host" />
    </intent-filter>
</activity>

3. Turn off Flutter's built-in deep linking

The plugin hands every link to the SDK and leaves it for other plugins too, so Flutter's own deep linking would also open Grovs links as routes. Add this inside the launcher activity:

XML
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />

iOS setup

1. Add configuration to Info.plist

Add to ios/Runner/Info.plist:

XML
<key>GrovsApiKey</key>
<string>YOUR_API_KEY</string>
<key>GrovsUseTestEnvironment</key>
<true/>
 
<!-- Optional: custom base URL for self-hosted backends -->
<key>GrovsBaseURL</key>
<string>https://sdk.example.com</string>
 
<!-- Optional: start disabled until the user gives consent (default true) -->
<key>GrovsEnabled</key>
<false/>
 
<!-- Optional: extra hosts accepted for clipboard deferred deep linking -->
<key>GrovsClipboardDomains</key>
<array>
    <string>links.example.com</string>
    <string>promo.example.com</string>
</array>

The plugin registers as an application delegate and a scene delegate, so launches, universal links and URL opens reach the SDK with no AppDelegate.swift or SceneDelegate changes. It never claims a link, so other plugins (login callbacks, say) still receive theirs. It also sets itself as Grovs.delegate and Grovs.inAppDelegate. Don't assign your own delegates natively, or deep links and in-app events stop reaching Dart.

2. Configure URL schemes

Add your URL scheme to Info.plist:

XML
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>your_app_scheme</string>
        </array>
    </dict>
</array>

3. Configure Associated Domains

  1. Open ios/Runner.xcworkspace in Xcode.
  2. Select the Runner target, then Signing & Capabilities.
  3. Click + Capability and add Associated Domains.
  4. Add applinks:your_app_host and applinks:your_app_test_host.

4. Turn off Flutter's built-in deep linking

Without this, Flutter's own deep linking also opens Grovs links as routes and can send links that have no matching route to Safari. Add to Info.plist:

XML
<key>FlutterDeepLinkingEnabled</key>
<false/>

5. App lifecycle

Nothing to add. The plugin works with the UIScene lifecycle (FlutterSceneDelegate, the default for new Flutter apps) and with AppDelegate-only apps. If you wrote scene delegate code that calls Grovs.handleSceneDelegate, remove it, or every link is delivered twice. If your scene delegate doesn't extend FlutterSceneDelegate, follow Flutter's UIScene migration guide so Flutter passes scene events to plugins.

For push campaigns, set the notification center delegate in AppDelegate.swift. See Push notifications.

Basic usage

Dart
import 'package:grovs_flutter_plugin/grovs.dart';
 
final grovs = Grovs();
 
// Optional: verbose logging while integrating ('info' or 'error')
await grovs.setDebugLevel('info');
 
// Optional: identify the user
await grovs.setUserIdentifier('user-123');
await grovs.setUserAttributes({
  'name': 'Jane Doe',
  'plan': 'premium',
});

Every method returns a Future and throws GrovsException when the native call fails. Only getPlatformVersion lets a raw PlatformException through. grovs.dart also exports the in-app message types. The link and error models (GenerateLinkParams, DeeplinkDetails, GrovsLinkDetails, GrovsException and the rest) need a second import:

Dart
import 'package:grovs_flutter_plugin/models/grovs_link.dart';

Next steps

See also

Last updated 2026-10-27
Was this page helpful?