こんにちは、株式会社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) {
// 省略
}
}
認証リンクではなくて認証コードによるメールアドレスの認証の方が、アプリを開く処理を実装しなくて済むのでベターかもしれませんね。
今後は認証コードでメールアドレスの所有権を確認する実装をしていきたいと思います。