Flutterのuni_linksを使ってみた
以前はてなブログで書いていた記事を、こちらに移しました。
Flutterでディープリンクを実装しようと思っており、実装方法を調べているとuni_linksというライブラリを見つけました。 今回はこのライブラリを使って、DeepLinksの実装をしていこうと思います。
uni_linksとは
ディープリンク(AndroidのAppLinks, DeepLinks、iOSのUniversalLinks CustomUrlScheme)を支援するプラグイン。
中ではディープリンクが各プラットフォームに届いた際に、 MethodChannel や EventChannel を用いて、Flutter側に通知しています。(違っていたらすみません。)
実装例
今回の実装例では、AndroidのDeepLinks、iOSのCustomUrlSchemeで実装していきます。
今回実装したコードはこちらです。
GitHub - idonuntius/DeepLink_Flutter
前提
遷移は下記の図の通りです。

MainPageからExample1Page、Example2Page、Example3Pageに遷移ができ、各Exampleのページは自身のページと各Exampleページに遷移ができます。
またExample1Page、Example2Pageではリンクのクエリを表示するようにしています。
インストール
pubspec.yml に下記を記載する。
# ...
dependencies:
# ...
uni_links: ^0.2.0
# ...
iOS, Android側の設定
AndroidとiOSにディープリンクの情報を記載する必要があります。
各プラットフォームでディープリンクを実装する時と同じ方法です。
iOS
ios/Runner/Info.plist に記載します。
<plist version="1.0">
<dict>
<!-- ↓↓ 追加 -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>example1</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.idonuntius.deeplinkflutter</string>
</array>
</dict>
</array>
<!-- ... -->
</dict>
Android
android/app/src/main/AndroidManifest.xml に記載します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.idonuntius.deep_link_flutter">
<application
android:name="io.flutter.app.FlutterApplication"
android:label="deep_link_flutter"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-->↓↓ 追加<-->
<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="com.idonuntius.deeplinkflutter" />
</intent-filter>
</activity>
<!-->...<-->
Flutter側の設定
Example1Page, Example2Page, Example3Pageの実装
今回は遷移したページを用意したいだけなので、3つとも StatelessWidget で実装。
example1_page.dart と example2_page.dart では、クエリを表示させたいので、 _query の値をコンストラクタで受け取る。
下記は example1_page.dart の例です。
import 'package:flutter/material.dart';
class Example1Page extends StatelessWidget {
final String _query;
Example1Page(this._query);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Example1',
),
),
body: Center(
child: Text(_query),
),
);
}
}
main.dartの修正
homeを MainPage() に変更しておきます。
import 'package:deep_link_flutter/view/main_page.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainPage(), // ← ここを変更
);
}
}
下準備は終わりです。 ではメインのMainPageを実装していきます。
MainPage
前提
MainPageの部分の実装は下記の図です。

下記のように実装していきます。
- MainViewModelでuni_linksのメソッドを使いディープリンクを受け取る
- 受け取ったディープリンクリンクをRoutePatternに変換
- RoutePatternをStreamで流す
- MainPageで受け取ったRoutePatternをもとに遷移
RoutePattern
下記のように実装。
sealed class的なものを作成しました。
example1, example2, example3用の値を作成し、example1, example2ではqueryのパラメータを持つようにしています。
abstract class RoutePattern {
RoutePattern();
factory RoutePattern.example1(final String query) = RoutePatternExample1;
factory RoutePattern.example2(final String query) = RoutePatternExample2;
factory RoutePattern.example3() = RoutePatternExample3;
R when<R>({
final R Function(RoutePatternExample1 state) example1,
final R Function(RoutePatternExample2 state) example2,
final R Function(RoutePatternExample3 state) example3,
}) {
if (this is RoutePatternExample1) {
return example1(this);
} else if (this is RoutePatternExample2) {
return example2(this);
} else if (this is RoutePatternExample3) {
return example3(this);
} else {
throw StateError;
}
}
}
class RoutePatternExample1 extends RoutePattern {
final String query;
RoutePatternExample1(this.query);
}
class RoutePatternExample2 extends RoutePattern {
final String query;
RoutePatternExample2(this.query);
}
class RoutePatternExample3 extends RoutePattern {}
MainViewModel
MainViewModelは下記のように実装。
class MainViewModel {
final _routePatternController = StreamController<RoutePattern>();
MainViewModel() {
_loadNextRoute();
}
void dispose() {
_routePatternController.close();
}
Stream<RoutePattern> get nextRoute => _routePatternController.stream;
void _loadNextRoute() {
getInitialUri().then((uri) {
_streamRoutePattern(uri);
});
getUriLinksStream().listen((uri) {
_streamRoutePattern(uri);
});
}
void _streamRoutePattern(Uri uri) {
if (uri != null) {
switch (uri.host) {
case 'example1':
_routePatternController.sink.add(RoutePattern.example1(uri.query));
break;
case 'example2':
_routePatternController.sink.add(RoutePattern.example2(uri.query));
break;
case 'example3':
_routePatternController.sink.add(RoutePattern.example3());
break;
}
}
}
}
ディープリンクのURLはuni_linksの getInitialUri() と getUriLinksStream() でUri型で受け取っています。2つのメソッドの説明は下記です。
getInitialUri: ディープリンクによってアプリが起動したとき呼ばれるgetUriLinksStream: アプリが起動している場合呼ばれる
受け取ったUriを _streamRoutePattern で RoutePattern に変換し、Streamで流します。
MainPage
下記のように実装しました。
class _MainPageState extends State<MainPage> {
final _mainViewModel = MainViewModel();
@override
void initState() {
super.initState();
_mainViewModel.nextRoute.listen((routePattern) {
Navigator.push(context, _nextPageRoute(routePattern));
});
}
@override
void dispose() {
_mainViewModel.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Main',
),
),
body: Container(),
);
}
Route _nextPageRoute(RoutePattern routePattern) {
Widget page;
routePattern.when(
example1: (pattern) => page = Example1Page(pattern.query),
example2: (pattern) => page = Example2Page(pattern.query),
example3: (pattern) => page = Example3Page(),
);
return MaterialPageRoute(builder: (context) => page);
}
}
MainViewModelの nextRoute から流れてきた RoutePattern を取得し、その値を元に _nextPageRoute でRoute型に変換し遷移処理をしています。
動作確認
ちゃんと遷移することを確認できました。

最後に
自分で実装するとなると結構大変なディープリンクですが、uni_linksを使うと簡単に実装することができました。
間違えている部分があった場合は、教えていただけると幸いです。
最後まで読んでいただき、ありがとうございました。