# Link creation

> Generate smart links and read link details in your Flutter app.

Source: https://docs.grovs.io/docs/sdk/flutter/link-creation

Create smart links from your app with `generateLink`, and read an existing link with `linkDetails`.

## Generating a link

Pass metadata, tracking and redirects. It returns the URL as a `String`:

```dart
import 'package:flutter/foundation.dart';
import 'package:grovs_flutter_plugin/grovs.dart';
import 'package:grovs_flutter_plugin/models/grovs_link.dart';

final grovs = Grovs();

Future<String> createShareLink() async {
  try {
    final link = await grovs.generateLink(
      GenerateLinkParams(
        title: 'Check out this product',
        subtitle: 'Limited time offer',
        imageURL: 'https://example.com/image.jpg',
        data: {
          'screen': 'product',
          'productId': '12345',
        },
        tags: ['promotion', 'share'],
        tracking: TrackingParams(
          utmCampaign: 'spring_sale',
          utmSource: 'in_app',
          utmMedium: 'share_button',
        ),
      ),
    );

    debugPrint('Generated: $link');
    return link;
  } on GrovsException catch (e) {
    debugPrint('Error: ${e.message} (${e.code})');
    rethrow;
  }
}
```

The SDK must be configured, enabled and authenticated. Failures throw `GrovsException` with `code` `GENERATION_ERROR`. The `message` is the native error text on Android, a generic message on iOS.

## Parameters

| Parameter                | Type                    | Description                                                                                                                                                                                                                                    |
| ------------------------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title`                  | `String`                | Link preview title (required)                                                                                                                                                                                                                  |
| `subtitle`               | `String?`               | Link preview subtitle                                                                                                                                                                                                                          |
| `imageURL`               | `String?`               | Link preview image URL                                                                                                                                                                                                                         |
| `data`                   | `Map<String, dynamic>?` | Custom payload delivered on deep link open                                                                                                                                                                                                     |
| `tags`                   | `List<String>?`         | Tags for filtering and analytics                                                                                                                                                                                                               |
| `customRedirects`        | `CustomRedirects?`      | Override the default redirect per platform. See [Custom redirects](#custom-redirects)                                                                                                                                                          |
| `showPreviewIos`         | `bool?`                 | Show the link preview page on iOS. `null` uses the project default                                                                                                                                                                             |
| `showPreviewAndroid`     | `bool?`                 | Show the link preview page on Android. `null` uses the project default                                                                                                                                                                         |
| `copyToClipboardIos`     | `bool?`                 | Copy the link to the clipboard from the preview page on iOS, for [clipboard-assisted deferred deep linking](https://docs.grovs.io/docs/sdk/flutter/deep-linking#clipboard-assisted-deferred-deep-linking). `null` inherits the project setting |
| `copyToClipboardAndroid` | `bool?`                 | Same, for Android. `null` inherits the project setting                                                                                                                                                                                         |
| `tracking`               | `TrackingParams?`       | UTM campaign, source and medium                                                                                                                                                                                                                |

`copyToClipboardIos` and `copyToClipboardAndroid` apply only when that platform shows the preview page. Passing `false` is an explicit override, unlike omitting the parameter. Project setting and visitor experience: [Copy to Clipboard](https://docs.grovs.io/docs/links/copy-to-clipboard).

On Android, `data` values that are strings, numbers, booleans or otherwise serializable pass through; anything else goes through `toString()`. iOS passes the map unchanged.

## Custom redirects

Override where a link sends users per platform:

```dart
final link = await grovs.generateLink(
  GenerateLinkParams(
    title: 'Special offer',
    data: {'promoId': 'summer25'},
    customRedirects: CustomRedirects(
      ios: CustomLinkRedirect(url: 'https://example.com/ios-promo'),
      android: CustomLinkRedirect(url: 'https://example.com/android-promo'),
      desktop: CustomLinkRedirect(
        url: 'https://example.com/desktop-promo',
        openAppIfInstalled: false,
      ),
    ),
  ),
);
```

With `openAppIfInstalled` `true` (default), the app opens if installed, otherwise the redirect URL is used. With `false`, the redirect URL is always used.

## Sharing the link

Open the share sheet once the link is ready, for example with [`share_plus`](https://pub.dev/packages/share_plus):

```dart
import 'package:share_plus/share_plus.dart';

final link = await grovs.generateLink(
  GenerateLinkParams(title: 'Share this', data: {'itemId': 'abc'}),
);
await Share.share(link);
```

## Link details

`linkDetails` fetches a link by its path, the last part of the link. For `https://yourapp.sqd.link/abc123` the path is `abc123`. Pass the path only, not the full URL.

```dart
try {
  final details = await grovs.linkDetails('abc123');
  debugPrint('${details.title} ${details.data} ${details.tracking?.utmCampaign}');
} on GrovsException catch (e) {
  debugPrint('Error ${e.code}: ${e.message}');
}
```

| `GrovsException.code` | Raised when                                            |
| --------------------- | ------------------------------------------------------ |
| `INVALID_ARGUMENT`    | The path is empty or only whitespace. Checked in Dart. |
| `SDK_DISABLED`        | The SDK is disabled with `setSDK(false)`.              |
| `LINK_DETAILS_ERROR`  | The link wasn't found or the request failed.           |

The result is a [`GrovsLinkDetails`](https://docs.grovs.io/docs/sdk/flutter/api-reference#grovslinkdetails). A field whose value has an unexpected type is `null`. `raw` holds every field the backend sent, as received; its numbers can be doubles on Android and ints on iOS, while the typed fields are the same on both platforms.

## Next steps

- [Handle incoming deep links →](https://docs.grovs.io/docs/sdk/flutter/deep-linking)
- [Full API reference →](https://docs.grovs.io/docs/sdk/flutter/api-reference)
