Create Dynamic Links in Flutter
Create dynamic or unified links programmatically from your Dart code using the ULink Flutter SDK.
Prerequisites
Before creating links, ensure you've completed the SDK setup:
SDK Integration
- Flutter SDK plugin installed and initialized
ULink.instance.initializecalled with valid configuration
ULink Dashboard Configuration
- API key generated from Dashboard → Settings → API Keys → Generate API Key
- Domain configured in your project (Domains → Add Domain)
If you haven't completed these steps, follow the Flutter Getting Started guide to set up the SDK and configure your project.
Understanding Link Types
Before creating links, understand the difference between the two types:
Unified Links
- Redirect users based on platform (iOS/Android/Desktop)
- Do not carry parameters into the app
- Best for: Marketing campaigns, App Store redirects, simple sharing
- Example use case: Share a link that opens the App Store on iOS, Play Store on Android, or a website on desktop
Dynamic Links
- Include structured parameters for deep linking
- Best for: In-app navigation, personalized content, tracking campaigns
- Example use case: Share a product link that opens directly to that product screen in your app
1. Create Dynamic Links
Dynamic links include parameters that your app can use for navigation and personalization.
Step 1.1: Build Link Parameters
Use the ULinkParameters.dynamic() factory constructor to create parameters:
final parameters = ULinkParameters.dynamic(
domain: 'links.shared.ly', // Your configured domain (required)
slug: 'promo-${DateTime.now().millisecondsSinceEpoch}', // Optional: unique identifier
fallbackUrl: 'https://example.com', // Web fallback (required)
iosFallbackUrl: 'https://apps.apple.com/app/id123', // iOS fallback (App Store)
androidFallbackUrl: 'https://play.google.com/store/apps/details?id=com.app', // Android fallback
parameters: {
'screen': 'promo',
'campaign': 'spring',
'productId': '12345',
},
socialMediaTags: SocialMediaTags(
ogTitle: 'Spring Sale - 50% Off!',
ogDescription: 'Don\'t miss our biggest sale of the year',
ogImage: 'https://example.com/sale-image.jpg',
),
);
domain: Must be a domain you've configured in your ULink dashboard. This is required for all link types.fallbackUrl: Required for dynamic links - where users land if the app isn't installed
- Slug: Make it descriptive and unique. If omitted, ULink generates one automatically.
- Parameters: Use these for in-app navigation (e.g.,
screen,productId,userId) - Social Media Tags: Improve link previews when shared on social platforms
- Fallback URLs: Always provide fallbacks so links work even when the app isn't installed
Step 1.2: Create the Link
Call createLink with your parameters. This is an async operation:
final sdk = ULink.instance;
Future<String?> createPromoLink() async {
try {
final response = await sdk.createLink(parameters);
if (response.success && response.url != null) {
debugPrint('Link created successfully: ${response.url}');
return response.url;
} else {
debugPrint('Failed to create link: ${response.error ?? "Unknown error"}');
return null;
}
} catch (e) {
debugPrint('Error creating link: $e');
return null;
}
}
Always check response.success before using the link. Common errors include:
- Invalid domain configuration
- Slug conflicts: If you use a slug that already exists in your domain, the API will return an error. Each slug must be unique per domain.
- Missing required parameters
- Network errors
If you receive a slug conflict error, either:
- Use a different slug for the new link
- Use the update API endpoint to modify the existing link with that slug
Response Structure
The createLink method returns a ULinkResponse with the following structure:
class ULinkResponse {
final bool success;
final String? url; // The created link URL
final String? error; // Error message if creation failed
final Map<String, dynamic>? data; // Additional response data
}
Example response:
ULinkResponse(
success: true,
url: 'https://links.shared.ly/promo-123abc',
error: null,
data: {
'slug': 'promo-123abc',
'shortUrl': 'https://links.shared.ly/promo-123abc',
},
)
2. Create Unified Links
Unified links are simpler - they just redirect based on platform without carrying parameters.
Step 2.1: Build Unified Link Parameters
final parameters = ULinkParameters.unified(
domain: 'links.shared.ly',
slug: 'app-download', // Optional
iosUrl: 'https://apps.apple.com/app/id123', // Required
androidUrl: 'https://play.google.com/store/apps/details?id=com.app', // Required
fallbackUrl: 'https://example.com', // Required
socialMediaTags: SocialMediaTags(
ogTitle: 'Download Our App',
ogDescription: 'Get the best experience with our mobile app',
ogImage: 'https://example.com/app-preview.jpg',
),
);
iosUrl: Required - where iOS users should be redirectedandroidUrl: Required - where Android users should be redirectedfallbackUrl: Required - where desktop users or fallback cases goparameters: Not supported in unified links (use dynamic links if you need parameters)
Step 2.2: Create the Unified Link
Future<String?> createUnifiedLink() async {
try {
final response = await ULink.instance.createLink(parameters);
if (response.success && response.url != null) {
debugPrint('Unified link created: ${response.url}');
return response.url;
} else {
debugPrint('Failed to create unified link: ${response.error}');
return null;
}
} catch (e) {
debugPrint('Error: $e');
return null;
}
}
3. Complete Example
Here's a complete example showing how to create and use a dynamic link in a Flutter widget:
import 'package:flutter/material.dart';
import 'package:flutter_ulink_sdk/flutter_ulink_sdk.dart';
import 'package:flutter_ulink_sdk/models/models.dart';
class ProductShareScreen extends StatefulWidget {
final String productId;
final String productName;
const ProductShareScreen({
super.key,
required this.productId,
required this.productName,
});
State<ProductShareScreen> createState() => _ProductShareScreenState();
}
class _ProductShareScreenState extends State<ProductShareScreen> {
final _sdk = ULink.instance;
String? _createdLink;
bool _isLoading = false;
String? _error;
Future<void> _createProductLink() async {
setState(() {
_isLoading = true;
_error = null;
_createdLink = null;
});
try {
final parameters = ULinkParameters.dynamic(
domain: 'links.shared.ly',
slug: 'product-${widget.productId}',
fallbackUrl: 'https://myshop.com/products/${widget.productId}',
iosFallbackUrl: 'https://apps.apple.com/app/myshop',
androidFallbackUrl: 'https://play.google.com/store/apps/details?id=com.myshop',
parameters: {
'screen': 'product',
'productId': widget.productId,
'productName': widget.productName,
},
socialMediaTags: SocialMediaTags(
ogTitle: widget.productName,
ogDescription: 'Check out ${widget.productName} in our app',
ogImage: 'https://myshop.com/images/${widget.productId}.jpg',
),
);
final response = await _sdk.createLink(parameters);
if (response.success && response.url != null) {
setState(() {
_createdLink = response.url;
_isLoading = false;
});
// Share the link
_shareLink(response.url!);
} else {
setState(() {
_error = response.error ?? 'Failed to create link';
_isLoading = false;
});
}
} catch (e) {
setState(() {
_error = 'Error: $e';
_isLoading = false;
});
}
}
void _shareLink(String url) {
// Implement your sharing logic here
// For example, using share_plus package:
// Share.share(url);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Share Product')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
widget.productName,
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: _isLoading ? null : _createProductLink,
child: _isLoading
? const CircularProgressIndicator()
: const Text('Create Share Link'),
),
if (_error != null) ...[
const SizedBox(height: 16),
Text(
_error!,
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
],
if (_createdLink != null) ...[
const SizedBox(height: 16),
Text('Link created:'),
SelectableText(_createdLink!),
],
],
),
),
);
}
}
Best Practices
Link Creation
- Slugs must be unique: Each slug can only be used once per domain. If you try to create a link with an existing slug, it will return an error. Use unique slugs for each link.
- Validate before sharing: Always check
response.successbefore sharing or storing the link - Handle errors gracefully: Provide user feedback if link creation fails, especially for slug conflicts
- Use descriptive slugs: Make slugs meaningful (e.g.,
product-12345instead of random strings) but ensure they're unique - Use async/await: Always use
awaitwhen callingcreateLinksince it's an async operation
Social Media Tags
- Always include OG tags: Better link previews improve click-through rates
- Use high-quality images: OG images should be at least 1200x630 pixels
- Write compelling descriptions: Keep descriptions under 200 characters for best display
Parameters
- Keep parameters simple: Use flat key-value pairs for best compatibility
- Avoid sensitive data: Don't put passwords, tokens, or PII in parameters
- Use consistent keys: Establish a naming convention (e.g.,
screen,productId,userId)
Async Operations
- Handle errors: Always wrap
createLinkcalls in try-catch blocks - Show loading states: Display loading indicators while creating links
- Provide user feedback: Show success or error messages to users
Next Steps
Once you've created links, learn how to handle them when users tap them:
- Receive Links → Flutter - Handle incoming links in your app
- Troubleshoot & Test Deep Links - Debug and verify your links