flutter_ulink_sdk 0.2.2 copy "flutter_ulink_sdk: ^0.2.2" to clipboard
flutter_ulink_sdk: ^0.2.2 copied to clipboard

Flutter SDK for creating and handling dynamic links, similar to Branch.io

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_ulink_sdk/flutter_ulink_sdk.dart';
import 'package:flutter_ulink_sdk/models/models.dart';
import 'dart:async';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _sdk = ULink.instance;
  bool _isInitialized = false;
  String _status = 'Not initialized';
  String? _sessionId;
  String? _installationId;
  ULinkResponse? _createdLink;
  ULinkResolvedData? _resolvedData;
  SessionState _sessionState = SessionState.idle;

  StreamSubscription<ULinkResolvedData>? _dynamicLinkSubscription;
  StreamSubscription<ULinkResolvedData>? _unifiedLinkSubscription;
  final List<String> _linkEvents = [];

  @override
  void initState() {
    super.initState();
    // Auto-initialize SDK for testing
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _initializeSDK();
    });
  }

  @override
  void dispose() {
    _dynamicLinkSubscription?.cancel();
    _unifiedLinkSubscription?.cancel();
    super.dispose();
  }

  void _setupLinkListeners() {
    debugPrint('DEBUG: Setting up link listeners');

    // Cancel existing subscriptions to prevent duplicates
    _dynamicLinkSubscription?.cancel();
    _unifiedLinkSubscription?.cancel();

    _dynamicLinkSubscription = _sdk.onDynamicLink.listen((linkData) {
      debugPrint('DEBUG: Dynamic link event received: ${linkData.toJson()}');
      setState(() {
        _linkEvents.insert(
          0,
          'Dynamic Link: ${linkData.slug ?? "Unknown"} - ${linkData.parameters}',
        );
        _resolvedData = linkData;
      });
      debugPrint('DEBUG: UI updated with dynamic link event');
    });

    _unifiedLinkSubscription = _sdk.onUnifiedLink.listen((linkData) {
      debugPrint('DEBUG: Unified link event received: ${linkData.toJson()}');
      setState(() {
        _linkEvents.insert(
          0,
          'Unified Link: ${linkData.slug ?? "Unknown"} - ${linkData.parameters}',
        );
        _resolvedData = linkData;
      });
      debugPrint('DEBUG: UI updated with unified link event');
    });
    debugPrint('DEBUG: Link listeners setup complete');
  }

  Future<void> _initializeSDK() async {
    if (_isInitialized) {
      debugPrint('DEBUG: SDK already initialized, skipping...');
      return;
    }

    debugPrint('DEBUG: Starting SDK initialization...');
    try {
      final config = ULinkConfig(
        apiKey:
            'ulk_5653d6d2c53cbbc7c1d09a621cf439782e795c0c437abee6', // Replace with your actual API key
        debug: true,
        enableDeepLinkIntegration:
            true, // Explicitly enable deep link integration
      );

      debugPrint(
        'DEBUG: Config created: ${config.apiKey}, ${config.baseUrl}, ${config.debug}',
      );

      await _sdk.initialize(config);
      debugPrint('DEBUG: SDK initialized successfully');

      final installationId = await _sdk.getInstallationId();
      debugPrint('DEBUG: Installation ID: $installationId');

      final sessionState = await _sdk.getSessionState();
      debugPrint('DEBUG: Session state: $sessionState');

      // Setup link listeners after SDK is initialized
      _setupLinkListeners();

      // Check for initial deep link after listeners are set up
      try {
        final initialData = await _sdk.getInitialDeepLink();
        if (initialData != null) {
          debugPrint('DEBUG: Initial deep link found: ${initialData.toJson()}');
          setState(() {
            _linkEvents.insert(
              0,
              'Initial Link: ${initialData.slug ?? "Unknown"} - ${initialData.parameters}',
            );
            _resolvedData = initialData;
          });
        }
      } catch (e) {
        debugPrint('DEBUG: Error getting initial deep link: $e');
      }

      setState(() {
        _isInitialized = true;
        _status = 'Initialized successfully';
        _installationId = installationId;
        _sessionState = sessionState;
      });
    } catch (e) {
      debugPrint('DEBUG: Initialization error: $e');
      setState(() {
        _status = 'Initialization failed: $e';
      });
    }
  }

  Future<void> _endSession() async {
    debugPrint('DEBUG: _endSession called');
    try {
      debugPrint('DEBUG: Calling SDK endSession');
      await _sdk.endSession();
      debugPrint('DEBUG: Session ended successfully');

      final sessionState = await _sdk.getSessionState();
      debugPrint('DEBUG: Session state after end: $sessionState');

      setState(() {
        _sessionId = null;
        _sessionState = sessionState;
        _status = 'Session ended';
      });
      debugPrint('DEBUG: UI updated after session end');
    } catch (e) {
      debugPrint('DEBUG: Failed to end session - Error: $e');
      debugPrint('DEBUG: Error type: ${e.runtimeType}');
      setState(() {
        _status = 'Failed to end session: $e';
      });
    }
  }

  Future<void> _createDynamicLink() async {
    debugPrint('DEBUG: _createDynamicLink called');
    try {
      final parameters = ULinkParameters.dynamic(
        domain: 'iossdk.shared.ly',
        slug: 'example-dynamic-link1234',
        iosFallbackUrl: 'https://apps.apple.com/app/your-app',
        androidFallbackUrl:
            'https://play.google.com/store/apps/details?id=your.app',
        fallbackUrl: 'https://your-website.com',
        parameters: {'product_id': '12345', 'campaign': 'summer_sale'},
        socialMediaTags: SocialMediaTags(
          ogTitle: 'Check out this amazing product!',
          ogDescription: 'Don\'t miss our summer sale with great discounts.',
          ogImage: 'https://your-website.com/images/product.jpg',
        ),
      );
      debugPrint(
        'DEBUG: Dynamic link parameters created: ${parameters.toJson()}',
      );

      debugPrint('DEBUG: Calling SDK createLink for dynamic link');
      final response = await _sdk.createLink(parameters);
      debugPrint(
        'DEBUG: createLink response - success: ${response.success}, url: ${response.url}, error: ${response.error}',
      );

      setState(() {
        _createdLink = response;
        if (response.success) {
          _status = 'Dynamic link created: ${response.url}';
        } else {
          _status = 'Failed to create link: ${response.error}';
        }
      });
      debugPrint('DEBUG: UI updated with created dynamic link');
    } catch (e) {
      debugPrint('DEBUG: Failed to create dynamic link - Error: $e');
      debugPrint('DEBUG: Error type: ${e.runtimeType}');
      setState(() {
        _status = 'Failed to create dynamic link: $e';
      });
    }
  }

  Future<void> _createUnifiedLink() async {
    debugPrint('DEBUG: _createUnifiedLink called');
    try {
      final parameters = ULinkParameters.unified(
        domain: 'libyanspider.shared.ly',
        slug: 'example-unified-link',
        iosUrl: 'https://apps.apple.com/app/your-app',
        androidUrl: 'https://play.google.com/store/apps/details?id=your.app',
        fallbackUrl: 'https://your-website.com',
        parameters: {'page': 'home', 'ref': 'unified_link'},
      );
      debugPrint(
        'DEBUG: Unified link parameters created: ${parameters.toJson()}',
      );

      debugPrint('DEBUG: Calling SDK createLink for unified link');
      final response = await _sdk.createLink(parameters);
      debugPrint(
        'DEBUG: createLink response - success: ${response.success}, url: ${response.url}, error: ${response.error}',
      );

      setState(() {
        _createdLink = response;
        if (response.success) {
          _status = 'Unified link created: ${response.url}';
        } else {
          _status = 'Failed to create link: ${response.error}';
        }
      });
      debugPrint('DEBUG: UI updated with created unified link');
    } catch (e) {
      debugPrint('DEBUG: Failed to create unified link - Error: $e');
      debugPrint('DEBUG: Error type: ${e.runtimeType}');
      setState(() {
        _status = 'Failed to create unified link: $e';
      });
    }
  }

  Future<void> _resolveLink() async {
    debugPrint('DEBUG: _resolveLink called');
    if (_createdLink?.url == null) {
      debugPrint('DEBUG: No link to resolve - _createdLink is null');
      setState(() {
        _status = 'No link to resolve. Create a link first.';
      });
      return;
    }

    try {
      debugPrint('DEBUG: Resolving link: ${_createdLink!.url}');
      final response = await _sdk.resolveLink(_createdLink!.url!);
      debugPrint('DEBUG: resolveLink response - success: ${response.success}');

      if (response.success && response.data != null) {
        final resolvedData = ULinkResolvedData.fromJson(response.data!);
        debugPrint(
          'DEBUG: Link resolved successfully: ${resolvedData.toJson()}',
        );

        setState(() {
          _resolvedData = resolvedData;
          _status = 'Link resolved successfully';
        });
        debugPrint('DEBUG: UI updated with resolved data');
      } else {
        setState(() {
          _status = 'Failed to resolve link: ${response.error}';
        });
      }
    } catch (e) {
      debugPrint('DEBUG: Failed to resolve link - Error: $e');
      debugPrint('DEBUG: Error type: ${e.runtimeType}');
      setState(() {
        _status = 'Failed to resolve link: $e';
      });
    }
  }

  Future<void> _getLastLinkData() async {
    debugPrint('DEBUG: _getLastLinkData called');
    try {
      debugPrint('DEBUG: Calling SDK getLastLinkData');
      final lastLinkData = await _sdk.getLastLinkData();
      debugPrint(
        'DEBUG: Last link data retrieved: ${lastLinkData?.toJson() ?? "null"}',
      );

      setState(() {
        _status = lastLinkData != null
            ? 'Last link data retrieved'
            : 'No last link data available';
      });
      debugPrint('DEBUG: UI updated with last link data');
    } catch (e) {
      debugPrint('DEBUG: Failed to get last link data - Error: $e');
      debugPrint('DEBUG: Error type: ${e.runtimeType}');
      setState(() {
        _status = 'Failed to get last link data: $e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ULink Bridge SDK Example',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('ULink Bridge SDK Example'),
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        ),
        body: SingleChildScrollView(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              // Status Card
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Status',
                        style: Theme.of(context).textTheme.titleLarge,
                      ),
                      const SizedBox(height: 8),
                      Text(_status),
                      const SizedBox(height: 8),
                      Text('Initialized: $_isInitialized'),
                      if (_installationId != null)
                        Text('Installation ID: $_installationId'),
                      if (_sessionId != null) Text('Session ID: $_sessionId'),
                      Text('Session State: ${_sessionState.value}'),
                    ],
                  ),
                ),
              ),

              const SizedBox(height: 16),

              // SDK Operations
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'SDK Operations',
                        style: Theme.of(context).textTheme.titleLarge,
                      ),
                      const SizedBox(height: 16),

                      ElevatedButton(
                        onPressed: _isInitialized ? null : _initializeSDK,
                        child: const Text('Initialize SDK'),
                      ),

                      const SizedBox(height: 8),

                      ElevatedButton(
                        onPressed: _isInitialized && _sessionId != null
                            ? _endSession
                            : null,
                        child: const Text('End Session (Manual)'),
                      ),

                      const SizedBox(height: 8),

                      Text(
                        'Note: Sessions are automatically managed by the SDK',
                        style: Theme.of(context).textTheme.bodySmall,
                      ),
                    ],
                  ),
                ),
              ),

              const SizedBox(height: 16),

              // Link Operations
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Link Operations',
                        style: Theme.of(context).textTheme.titleLarge,
                      ),
                      const SizedBox(height: 16),

                      Row(
                        children: [
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isInitialized
                                  ? _createDynamicLink
                                  : null,
                              child: const Text('Create Dynamic Link'),
                            ),
                          ),
                          const SizedBox(width: 8),
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isInitialized
                                  ? _createUnifiedLink
                                  : null,
                              child: const Text('Create Unified Link'),
                            ),
                          ),
                        ],
                      ),

                      const SizedBox(height: 8),

                      Row(
                        children: [
                          Expanded(
                            child: ElevatedButton(
                              onPressed:
                                  _isInitialized &&
                                      _createdLink?.success == true
                                  ? _resolveLink
                                  : null,
                              child: const Text('Resolve Link'),
                            ),
                          ),
                          const SizedBox(width: 8),
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isInitialized
                                  ? _getLastLinkData
                                  : null,
                              child: const Text('Get Last Link Data'),
                            ),
                          ),
                        ],
                      ),

                      if (_createdLink != null) ...[
                        const SizedBox(height: 16),
                        Text(
                          'Created Link Response:',
                          style: Theme.of(context).textTheme.titleMedium,
                        ),
                        const SizedBox(height: 4),
                        Text('Success: ${_createdLink!.success}'),
                        if (_createdLink!.url != null)
                          SelectableText(
                            'URL: ${_createdLink!.url}',
                            style: Theme.of(context).textTheme.bodySmall,
                          ),
                        if (_createdLink!.error != null)
                          Text(
                            'Error: ${_createdLink!.error}',
                            style: TextStyle(color: Colors.red),
                          ),
                        if (_createdLink!.data != null)
                          Text(
                            'Data: ${_createdLink!.data?.keys.length ?? 0} fields',
                            style: Theme.of(context).textTheme.bodySmall,
                          ),
                      ],
                    ],
                  ),
                ),
              ),

              const SizedBox(height: 16),

              // Resolved Data
              if (_resolvedData != null)
                Card(
                  child: Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          'Resolved Link Data',
                          style: Theme.of(context).textTheme.titleLarge,
                        ),
                        const SizedBox(height: 8),
                        Text('Slug: ${_resolvedData!.slug ?? "N/A"}'),
                        Text('Link Type: ${_resolvedData!.linkType.name}'),
                        Text(
                          'Fallback URL: ${_resolvedData!.fallbackUrl ?? "N/A"}',
                        ),
                        if (_resolvedData!.parameters != null)
                          Text('Parameters: ${_resolvedData!.parameters}'),
                        if (_resolvedData!.socialMediaTags != null) ...[
                          const SizedBox(height: 8),
                          Text(
                            'Social Media Tags:',
                            style: Theme.of(context).textTheme.titleMedium,
                          ),
                          Text(
                            'Title: ${_resolvedData!.socialMediaTags!.ogTitle ?? "N/A"}',
                          ),
                          Text(
                            'Description: ${_resolvedData!.socialMediaTags!.ogDescription ?? "N/A"}',
                          ),
                          Text(
                            'Image: ${_resolvedData!.socialMediaTags!.ogImage ?? "N/A"}',
                          ),
                        ],
                      ],
                    ),
                  ),
                ),

              const SizedBox(height: 16),

              // Link Events
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Link Events (${_linkEvents.length})',
                        style: Theme.of(context).textTheme.titleLarge,
                      ),
                      const SizedBox(height: 8),
                      if (_linkEvents.isEmpty)
                        const Text('No link events received yet')
                      else
                        SizedBox(
                          height: 200,
                          child: ListView.builder(
                            itemCount: _linkEvents.length,
                            itemBuilder: (context, index) {
                              return Padding(
                                padding: const EdgeInsets.symmetric(
                                  vertical: 2.0,
                                ),
                                child: Text(
                                  _linkEvents[index],
                                  style: Theme.of(context).textTheme.bodySmall,
                                ),
                              );
                            },
                          ),
                        ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
3
likes
0
points
419
downloads

Publisher

verified publisherbeingthere.dev

Weekly Downloads

Flutter SDK for creating and handling dynamic links, similar to Branch.io

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on flutter_ulink_sdk

Packages that implement flutter_ulink_sdk