Flutter SDK
The ULink Flutter SDK provides seamless integration of linking functionality into your Flutter applications. Create, manage, and track links while handling deep links with ease.
Installation
Add the ULink Flutter SDK to your pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
ulink_flutter: ^1.0.0
Then run:
flutter pub get
Deep Link Handling Setup
Configure URL Schemes:
Replace {{yourdomain.com}}
with your actual domain and {{myapp}}
with your custom scheme.
For iOS, add to ios/Runner/Info.plist
:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>{{myapp}}.deeplink</string>
<key>CFBundleURLSchemes</key>
<array>
<string>{{myapp}}</string>
</array>
</dict>
</array>
For Android, add to android/app/src/main/AndroidManifest.xml
:
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme">
<!-- Standard App Link -->
<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="{{yourdomain.com}}" />
</intent-filter>
<!-- Custom URL Scheme -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="{{myapp}}" />
</intent-filter>
</activity>
- Domain (
{{yourdomain.com}}
): Found in your ULink dashboard under Project → Domains - Custom scheme (
{{myapp}}
): Found in your ULink dashboard under Project → Settings → Configure
Quick Start
1. Initialize the SDK
import 'package:flutter_ulink_sdk/flutter_ulink_sdk.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize ULink SDK
await ULink.initialize(
config: ULinkConfig(
apiKey: '{{your-api-key}}',
debug: true, // Enable for development
),
);
runApp(MyApp());
}
2. Create Your First Link
// Create a dynamic link with social media tags and parameters
final response = await ULink.instance.createLink(
ULinkParameters.dynamic(
slug: 'summer-sale',
iosFallbackUrl: 'myapp://product/summer-sale',
androidFallbackUrl: 'myapp://product/summer-sale',
fallbackUrl: 'https://{{yoursite.com}}/summer-sale',
// Add social media tags for better sharing
socialMediaTags: SocialMediaTags(
ogTitle: 'Summer Sale - Up to 50% Off!',
ogDescription: 'Don\'t miss our biggest summer sale with amazing discounts on all products.',
ogImage: 'https://{{yoursite.com}}/images/summer-sale-banner.jpg',
),
// Add custom parameters for tracking and deep linking
parameters: {
'utm_source': 'mobile_app',
'utm_campaign': 'summer_sale_2024',
'product_category': 'electronics',
'discount_code': 'SUMMER50',
},
),
);
if (response.success) {
print('Link created: ${response.url}');
// Access additional response data if needed
print('Response data: ${response.data}');
} else {
print('Error: ${response.error}');
}
3. Handle Deep Links
class MyApp extends StatefulWidget {
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
void initState() {
super.initState();
// Listen for incoming dynamic links
ULink.instance.onLink.listen((ULinkResolvedData resolvedData) {
handleDeepLink(resolvedData);
});
// Listen for unified links (external redirects)
ULink.instance.onUnifiedLink.listen((ULinkResolvedData resolvedData) {
// Handle unified link if needed
print('Unified link received: ${resolvedData.fallbackUrl}');
});
}
void handleDeepLink(ULinkResolvedData resolvedData) {
// Navigate based on the resolved data
final parameters = resolvedData.parameters;
if (parameters != null) {
// Extract navigation info from parameters
final productId = parameters['product_id'];
if (productId != null) {
Navigator.pushNamed(context, '/product', arguments: productId);
}
}
}
Widget build(BuildContext context) {
return MaterialApp(
title: 'ULink Demo',
home: HomeScreen(),
);
}
}
Deep Link Handling
Always check for null parameters and provide fallback navigation to ensure a smooth user experience.
Handle Deep Link Parameters:
void handleDeepLink(ULinkResolvedData resolvedData) {
final parameters = resolvedData.parameters;
if (parameters != null) {
// Check for product navigation
if (parameters.containsKey('product_id')) {
final productId = parameters['product_id'];
Navigator.pushNamed(context, '/product', arguments: productId);
return;
}
// Check for user profile navigation
if (parameters.containsKey('user_id')) {
final userId = parameters['user_id'];
Navigator.pushNamed(context, '/profile', arguments: userId);
return;
}
// Check for campaign tracking
if (parameters.containsKey('campaign')) {
final campaign = parameters['campaign'];
// Track campaign analytics
print('Campaign: $campaign');
}
}
// Default navigation
Navigator.pushNamed(context, '/home');
}
Error Handling
try {
final response = await ULink.instance.createLink(
ULinkParameters.unified(
slug: 'test-link',
iosUrl: 'https://apps.apple.com/app/{{yourapp}}/id{{123456}}',
androidUrl: 'https://play.google.com/store/apps/details?id={{com.yourapp}}',
fallbackUrl: 'https://{{yoursite.com}}/test',
),
);
if (response.success) {
print('Link created: ${response.url}');
// Use response.data for additional information if needed
} else {
print('Error creating link: ${response.error}');
}
} catch (e) {
print('Unexpected error: $e');
}
Best Practices
1. Initialize Early:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize as early as possible
await ULink.initialize(
config: ULinkConfig(
apiKey: '{{your-api-key}}',
baseUrl: 'https://api.ulink.ly',
debug: true, // Enable for development
),
);
runApp(MyApp());
}
2. Handle Subscription Lifecycle Correctly:
Always cancel stream subscriptions in the dispose() method to prevent memory leaks. The SDK automatically manages sessions, but you must manage your own subscriptions.
// The SDK automatically manages sessions based on app lifecycle
// Sessions start when app becomes active and end when app goes to background
// However, you must properly manage stream subscriptions to prevent memory leaks
class MyApp extends StatefulWidget {
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
StreamSubscription<ULinkResolvedData>? _linkSubscription;
void initState() {
super.initState();
// Set up link listeners with proper subscription management
_linkSubscription = ULink.instance.onLink.listen(
(resolvedData) {
// Handle dynamic links
handleDeepLink(resolvedData);
},
onError: (error) {
// Handle subscription errors
print('Link subscription error: $error');
},
);
}
void dispose() {
// Always cancel subscriptions to prevent memory leaks
_linkSubscription?.cancel();
super.dispose();
}
void handleDeepLink(ULinkResolvedData resolvedData) {
// Your deep link handling logic
print('Received deep link: ${resolvedData.url}');
// Navigate based on the resolved data
if (resolvedData.parameters.containsKey('screen')) {
// Navigate to specific screen
Navigator.pushNamed(context, resolvedData.parameters['screen']);
}
}
Widget build(BuildContext context) {
return MaterialApp(
// Your app configuration
);
}
}
Alternative: Using StatelessWidget with Provider/Riverpod:
// For better state management, consider using a dedicated service
class DeepLinkService {
StreamSubscription<ULinkResolvedData>? _linkSubscription;
void initialize() {
_linkSubscription = ULink.instance.onLink.listen(
(resolvedData) => _handleDeepLink(resolvedData),
onError: (error) => print('Link error: $error'),
);
}
void dispose() {
_linkSubscription?.cancel();
}
void _handleDeepLink(ULinkResolvedData resolvedData) {
// Centralized deep link handling
}
}
// In your main app
class MyApp extends StatelessWidget {
final DeepLinkService _deepLinkService = DeepLinkService();
MyApp() {
_deepLinkService.initialize();
}
Widget build(BuildContext context) {
return MaterialApp(
// Your app configuration
);
}
}
3. Error Handling:
Always check the response.success property and handle both API errors and exceptions to provide a better user experience.
try {
final response = await ULink.instance.createLink(
ULinkParameters.dynamic(
slug: 'my-link',
fallbackUrl: 'https://{{mysite.com}}',
),
);
if (response.success) {
print('Link created: ${response.url}');
// Access additional data if needed: response.data
} else {
print('Error: ${response.error}');
}
} catch (e) {
print('Exception: $e');
}
Support
Need help with the Flutter SDK?
- GitHub Issues - Report bugs or request features
- API Documentation - REST API reference
- Support Email - Get help from our team
Next Steps
- API Reference - Learn about the REST API
- Dashboard - Manage your links and view analytics
You're ready to start creating and managing links with the ULink Flutter SDK!