URLSchemaからアプリを起動する方法

こんにちは、株式会社Pentagon代表の山本です。
当社では「アプリを駆使した事業を立ち上げたい」方のために、アプリの設計・デザイン・開発までまるっとサポートしています。
今回は、メールアドレスの認証リンクを踏んだ後にアプリを開きたいケースにて、URLSchemaを使ってアプリを開く方法をまとめます。

pentagon://pentagon.tokyo/email_authorized

Web上でこのようなURLを用意して、タップするとアプリを開くようにしていきます。

目次

uni_linksを使う

現在開発中のアプリでは、まだnull_safetyに対応をできていないため、少し古いバージョンを使います。

uni_links: ^0.4.0

android/app/src/main/AndroidManifest.xmlに追記

           <!-- Deep Links -->
           <intent-filter>
               <action android:name="android.intent.action.VIEW" />
               <category android:name="android.intent.category.DEFAULT" />
               <category android:name="android.intent.category.BROWSABLE" />
               <!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
               <data
                 android:scheme="pentagon"
                 android:host="pentagon.tokyo" />
           </intent-filter>

           <!-- App Links -->
           <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" />
               <!-- Accepts URIs that begin with https://YOUR_HOST -->
               <data
                 android:scheme="https"
                 android:host="pentagon" />
           </intent-filter>

ios/Runner/Info.plistに追記

<key>CFBundleURLTypes</key>
     <array>
       <dict>
         <key>CFBundleTypeRole</key>
         <string>Editor</string>
         <key>CFBundleURLName</key>
         <string>pentagon.tokyo/email_authorized</string>
         <key>CFBundleURLSchemes</key>
         <array>
           <string>pentagon</string>
         </array>
       </dict>
     </array>

lib/main.dartを修正

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() {
    return new _MyAppState();
  }
}

enum UniLinksType { string, uri }

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  String _initialLink;
  Uri _initialUri;
  String _latestLink = 'Unknown';
  Uri _latestUri;
  StreamSubscription _sub;
  UniLinksType _type = UniLinksType.string;

  @override
  initState() {
    super.initState();
    initPlatformState();
  }

  @override
  dispose() {
    if (_sub != null) _sub.cancel();
    super.dispose();
  }

 // Platform messages are asynchronous, so we initialize in an async method.
 Future<void> initPlatformState() async {
   if (_type == UniLinksType.string) {
     await initPlatformStateForStringUniLinks();
   } else {
     await initPlatformStateForUriUniLinks();
   }
 }

 /// An implementation using a [String] link
 Future<void> initPlatformStateForStringUniLinks() async {
   // Attach a listener to the links stream
   _sub = getLinksStream().listen((String link) {
     if (!mounted) return;
     setState(() {
       _latestLink = link ?? 'Unknown';
       _latestUri = null;
       try {
         if (link != null) _latestUri = Uri.parse(link);
       } on FormatException {}
     });
   }, onError: (Object err) {
     if (!mounted) return;
     setState(() {
       _latestLink = 'Failed to get latest link: $err.';
       _latestUri = null;
     });
   });

   // Attach a second listener to the stream
   getLinksStream().listen((String link) {
     Logger.log('initPlatformStateForStringUniLinks: $link');

     //pentagon://pentagon.tokyo/email_authorized
     if (link?.contains('email_authorized') ?? false) {
       Get.offNamed("Eメール認証後のルート");
     }
   }, onError: (Object err) {
     Logger.log('got err: $err');
   });

   // Get the latest link
   // Platform messages may fail, so we use a try/catch PlatformException.
   try {
     _initialLink = await getInitialLink();
     Logger.log('initial link: $_initialLink');
     if (_initialLink != null) _initialUri = Uri.parse(_initialLink);
   } on PlatformException {
     _initialLink = 'Failed to get initial link.';
     _initialUri = null;
   } on FormatException {
     _initialLink = 'Failed to parse the initial link as Uri.';
     _initialUri = null;
   }

   // If the widget was removed from the tree while the asynchronous platform
   // message was in flight, we want to discard the reply rather than calling
   // setState to update our non-existent appearance.
   if (!mounted) return;

   setState(() {
     _latestLink = _initialLink;
     _latestUri = _initialUri;
   });
 }

 /// An implementation using the [Uri] convenience helpers
 Future<void> initPlatformStateForUriUniLinks() async {
   // Attach a listener to the Uri links stream
   _sub = getUriLinksStream().listen((Uri uri) {
     if (!mounted) return;
     setState(() {
       _latestUri = uri;
       _latestLink = uri?.toString() ?? 'Unknown';
     });
   }, onError: (Object err) {
     if (!mounted) return;
     setState(() {
       _latestUri = null;
       _latestLink = 'Failed to get latest link: $err.';
     });
   });

   // Attach a second listener to the stream
   getUriLinksStream().listen((Uri uri) {
     Logger.log('got uri: ${uri?.path} ${uri?.queryParametersAll}');
   }, onError: (Object err) {
     Logger.log('got err: $err');
   });

   // Get the latest Uri
   // Platform messages may fail, so we use a try/catch PlatformException.
   try {
     _initialUri = await getInitialUri();
     Logger.log('initial uri: ${_initialUri?.path}'
         ' ${_initialUri?.queryParametersAll}');
     _initialLink = _initialUri?.toString();
   } on PlatformException {
     _initialUri = null;
     _initialLink = 'Failed to get initial uri.';
   } on FormatException {
     _initialUri = null;
     _initialLink = 'Bad parse the initial link as Uri.';
   }

   // If the widget was removed from the tree while the asynchronous platform
   // message was in flight, we want to discard the reply rather than calling
   // setState to update our non-existent appearance.
   if (!mounted) return;

   setState(() {
     _latestUri = _initialUri;
     _latestLink = _initialLink;
   });
  }

  @override
  Widget build(BuildContext context) {
     // 省略
  }
}

認証リンクではなくて認証コードによるメールアドレスの認証の方が、アプリを開く処理を実装しなくて済むのでベターかもしれませんね。
今後は認証コードでメールアドレスの所有権を確認する実装をしていきたいと思います。

採用情報はこちら
目次