FlutterにおけるDeep linkについて:Nativeから出発

投稿者: | 2024年6月17日

Flutterでのディープリンク対応

最近のMobileアプリ開発ではディープリンク対応を求められる事が増えている。Flutterの場合、どうやるのかについて記載する。基本的にはiOSとAndroidのやり方に準拠するので、まずはそれぞれについて記載してからFlutterの場合に移る。

SwiftUI (iOS)

1. Associated Domainsの設定

まず、Xcodeでプロジェクトの「Signing & Capabilities」タブに移動し、「+ Capability」ボタンをクリックして「Associated Domains」を追加する。以下の形式でドメインを追加する。

applinks:yourdomain.com

2. apple-app-site-associationファイルの作成

ドメインのルートに apple-app-site-association ファイルを配置する。

{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "<TeamID>.<bundleID>",
                "paths": [ "/example/*" ]
            }
        ]
    }
}

3. SwiftUIでのディープリンクの処理

App構造体でonOpenURLを使用してリンクを処理する。

import SwiftUI

@main
struct DeepLinkApp: App {
    @State private var deepLinkID: String?

    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { url in
                    if let id = url.pathComponents.last {
                        deepLinkID = id
                    }
                }
                .environmentObject(DeepLinkManager(deepLinkID: $deepLinkID))
        }
    }
}

class DeepLinkManager: ObservableObject {
    @Binding var deepLinkID: String?

    init(deepLinkID: Binding<String?>) {
        _deepLinkID = deepLinkID
    }
}

struct ContentView: View {
    @EnvironmentObject var deepLinkManager: DeepLinkManager

    var body: some View {
        NavigationView {
            VStack {
                if let id = deepLinkManager.deepLinkID {
                    Text("Deep Link ID: \(id)")
                } else {
                    Text("No Deep Link")
                }
            }
            .navigationTitle("Deep Link Example")
        }
    }
}

Jetpack Compose (Android)

1. AndroidManifest.xmlの設定

AndroidManifest.xmlに<intent-filter>を追加してディープリンクを設定する。

<activity android:name=".MainActivity">
    <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="www.example.com"
              android:pathPrefix="/example" />
    </intent-filter>
</activity>

2. Jetpack Composeでのディープリンクの処理

MainActivityで受信したインテントを処理する。

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.deeplink.ui.theme.DeepLinkTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            var deepLinkID by remember { mutableStateOf<String?>(null) }

            handleIntent(intent) { id ->
                deepLinkID = id
            }

            DeepLinkTheme {
                Box(
                    modifier = Modifier.fillMaxSize(),
                    contentAlignment = Alignment.Center
                ) {
                    if (deepLinkID != null) {
                        Text(text = "Deep Link ID: $deepLinkID")
                    } else {
                        Text(text = "No Deep Link")
                    }
                }
            }
        }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        handleIntent(intent) { id ->
            deepLinkID = id
        }
    }

    private fun handleIntent(intent: Intent, onDeepLinkReceived: (String?) -> Unit) {
        val action = intent.action
        val data: Uri? = intent.data

        if (Intent.ACTION_VIEW == action && data != null) {
            val id = data.lastPathSegment
            onDeepLinkReceived(id)
        } else {
            onDeepLinkReceived(null)
        }
    }
}

Flutter

1. pubspec.yamlの設定

pubspec.yamlに必要な依存関係を追加する。

dependencies:
  flutter:
    sdk: flutter
  uni_links: ^0.5.1
  url_launcher: ^6.0.9

2. プラットフォーム固有の設定

  • Android:
    • 前述のAndroidセクションに示したように、AndroidManifest.xmlを更新する。
  • iOS:
    • iOSでのユニバーサルリンクの設定手順に従う。

3. Flutterでのリンク処理

Flutterコード内で、受信したリンクを処理する。

import 'package:flutter/material.dart';
import 'package:uni_links/uni_links.dart';
import 'package:url_launcher/url_launcher.dart';

void main() => runApp(MyApp());

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

class _MyAppState extends State<MyApp> {
  String? _deepLink;

  @override
  void initState() {
    super.initState();
    initUniLinks();
  }

  Future<void> initUniLinks() async {
    try {
      final initialLink = await getInitialLink();
      if (initialLink != null) {
        setState(() {
          _deepLink = initialLink;
        });
      }

      linkStream.listen((String? link) {
        setState(() {
          _deepLink = link;
        });
      }, onError: (err) {
        // エラーを処理する
      });
    } on PlatformException {
      // 例外を処理する
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('ディープリンクの例'),
        ),
        body: Center(
          child: _deepLink != null
              ? Text('ディープリンクID: $_deepLink')
              : Text('ディープリンクなし'),
        ),
      ),
    );
  }
}

これらのサンプルは、SwiftUIやJetpack Compose、Flutterを使用してディープリンクを処理する基本的な方法を示している。これにより、ユーザーが外部リンクからアプリ内の特定のコンテンツにシームレスに移動できるようになる。

コメントを残す