Flutterのuni_linksを使ってみた
久しぶりにブログ書きます!
Flutterでディープリンクを実装しようと思っており、実装方法を調べているとuni_linksというライブラリを見つけました。 今回はこのライブラリを使って、DeepLinksの実装をしていこうと思います。
uni_linksとは
ディープリンク(AndroidのAppLinks, DeepLinks、iOSのUniversalLinks CustomUrlScheme)を支援するプラグイン。
中ではディープリンクが各プラットフォームに届いた際に、 MethodChannel
や EventChannel
を用いて、Flutter側に通知しています。(違っていたらすみません。)
実装例
今回の実装例では、AndroidのDeepLinks、iOSのCustomUrlSchemeで実装していきます。
今回実装したコードはこちらです。 github.com
前提
遷移は下記の図の通りです。
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型ではなくString型で返すメソッドやStreamではなくFutureで返すメソッドなどもあるので、uni_linksのコードを確認してみてください。
受け取った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を使うと簡単に実装することができました。
間違えている部分があった場合は、教えていただけると幸いです。
最後まで読んでいただき、ありがとうございました。