← Назад к вопросам

Что такое Deeplink?

2.2 Middle🔥 191 комментариев
#Навигация#Нативная интеграция

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Deeplink

Определение

Deeplink - это специальная ссылка, которая открывает приложение и ведёт пользователя прямо на нужный экран, минуя главную страницу. Это похоже на обычные веб-ссылки, но для мобильных приложений.

Примеры deeplinks

Обычная ссылка:
https://example.com/products/123

Deeplink приложения:
myapp://products/123
или
https://example.com/products/123 (Universal Link / App Link)

Типы deeplinks

1. Custom Scheme Deeplinks (старый способ)

myapp://products/123
myapp://user/john@example.com
myapp://open?screen=settings&theme=dark

Проблема: браузер не знает, существует ли приложение с таким схемой. Может показать ошибку.

2. Universal Links (iOS) и App Links (Android) - новый способ

https://example.com/products/123
https://example.com/user/john

Преимущество: если приложение не установлено, ссылка откроется в браузере и покажет веб-версию.

Реализация в Flutter

Шаг 1: Настройка iOS

// ios/Runner/Info.plist
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLName</key>
    <string>com.example.myapp</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>

Шаг 2: Настройка Android

<!-- android/app/src/main/AndroidManifest.xml -->
<activity
  android:name=".MainActivity"
  android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
  
  <!-- Custom 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>

Шаг 3: Обработка deeplinks в Flutter

import 'package:uni_links/uni_links.dart';
import 'package:uni_links_web/uni_links_web.dart';

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late StreamSubscription deepLinkSubscription;
  final navigatorKey = GlobalKey<NavigatorState>();
  
  @override
  void initState() {
    super.initState();
    _initDeepLinkListener();
  }
  
  void _initDeepLinkListener() {
    // Слушаем входящие deeplinks пока приложение в памяти
    deepLinkSubscription = uriLinkStream.listen(
      (String? link) {
        if (link != null) {
          _handleDeepLink(link);
        }
      },
      onError: (err) {
        print('Deep link error: $err');
      },
    );
    
    // Проверяем, если приложение было запущено с deeplink
    _checkInitialDeepLink();
  }
  
  Future<void> _checkInitialDeepLink() async {
    try {
      final initialLink = await getInitialUri();
      if (initialLink != null) {
        _handleDeepLink(initialLink.toString());
      }
    } catch (e) {
      print('Error getting initial link: $e');
    }
  }
  
  void _handleDeepLink(String link) {
    print('Handling deeplink: $link');
    
    // Парсим ссылку
    final uri = Uri.parse(link);
    
    if (uri.scheme == 'myapp') {
      // Обработка custom scheme deeplinks
      final path = uri.path; // /products/123
      final id = uri.pathSegments.last; // 123
      
      navigatorKey.currentState?.pushNamed(
        '/products',
        arguments: ProductArgs(id: id),
      );
    } else if (uri.host == 'example.com') {
      // Обработка universal links
      final pathSegments = uri.pathSegments;
      
      if (pathSegments.first == 'products') {
        final id = pathSegments.last;
        navigatorKey.currentState?.pushNamed(
          '/products',
          arguments: ProductArgs(id: id),
        );
      } else if (pathSegments.first == 'user') {
        final username = pathSegments.last;
        navigatorKey.currentState?.pushNamed(
          '/user',
          arguments: UserArgs(username: username),
        );
      }
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey,
      home: HomePage(),
      onGenerateRoute: (settings) {
        // Роутинг приложения
        if (settings.name == '/products') {
          final args = settings.arguments as ProductArgs;
          return MaterialPageRoute(
            builder: (_) => ProductDetailScreen(productId: args.id),
          );
        }
        return null;
      },
    );
  }
  
  @override
  void dispose() {
    deepLinkSubscription.cancel();
    super.dispose();
  }
}

class ProductArgs {
  final String id;
  ProductArgs({required this.id});
}

Практические примеры

Приглашение в чат:

myapp://chat/room_id_123
myapp://chat/room_id_123?invite_code=ABC123

Поделиться профилем:

myapp://user/john_doe
https://example.com/user/john_doe

Специальное предложение:

myapp://promo?code=SUMMER20&discount=30
https://example.com/promo/SUMMER20

Тестирование deeplinks

На iOS:

# Эмулятор
xcrun simctl openurl booted "myapp://products/123"

# Реальное устройство
ios-deploy -b /path/to/app.app -o /path/to/app --args "myapp://products/123"

На Android:

# Эмулятор или устройство
adb shell am start -W -a android.intent.action.VIEW -d "myapp://products/123" com.example.myapp

Из браузера: Просто напечатай deeplink в адресную строку:

myapp://products/123

Advanced: Dynamic Links (Firebase)

Для более сложных сценариев используй Firebase Dynamic Links:

import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';

FirebaseDynamicLinks dynamicLinks = FirebaseDynamicLinks.instance;

void initDynamicLinks() async {
  dynamicLinks.onLink.listen(
    (PendingDynamicLinkData? dynamicLinkData) async {
      final Uri deepLink = dynamicLinkData?.link ?? Uri.parse('');
      
      if (deepLink.path.isNotEmpty) {
        _handleDeepLink(deepLink.toString());
      }
    },
    onError: (e) async {
      print('Dynamic Links error: ${e.message}');
    },
  );
  
  // Получить первоначальный dynamic link
  final PendingDynamicLinkData? initialLink = 
    await dynamicLinks.getInitialLink();
  if (initialLink?.link != null) {
    _handleDeepLink(initialLink!.link.toString());
  }
}

Преимущества deeplinks

  • User Experience - пользователь попадает прямо на контент
  • Marketing - можно делиться прямыми ссылками в соцсетях
  • Analytics - отслеживаешь, откуда приходят пользователи
  • Реф программы - друзья приглашают друзей по deeplinks

Best practices

  1. Обработай случаи, когда deeplink невалиден
  2. Если приложение не установлено - перенаправь на веб-версию
  3. Тестируй на реальных устройствах - поведение может отличаться
  4. Используй универсальные ссылки - надёжнее, чем custom schemes
  5. Документируй все deeplinks - для команды и маркетинга
Что такое Deeplink? | PrepBro