以下の記事の続きです。
Fltuterに初めてのFirebase導入(Firebase Analytics)
前回は接続確認だけしましたが、せっかくなので、イベントの送信もやってみます。
環境など
ツールなど | バージョンなど |
---|---|
MacBook Air Early2015 | macOS Mojave 10.14.5 |
Android Studio | 3.6.1 |
Java | 1.8.0_131 |
Flutter | 1.12.13+hotfix.5 |
Dart | 2.7.0 |
Xcode | 10.2 |
送信イベント
以下の2つを送ります。
- 画面名(setCurrentScreen)
- ボタンタップ(logEvent)
他にも課金イベントとかも用意されています。
詳しくは公式のサンプルなどをご覧下さい。
事前準備
DIのパッケージの1つである、get_itを使います。
Providerパターンにしてもいいかなと思ったのですが、なんとなく、get_itも使い慣れておきたくて。
というかこちらのパッケージを駆使すれば、Providerパターンも正直不要かな?
ChangeNotifierProvider
は要るでしょうけど、BLoC+Providerのパターンは書き変えられそうですね。
画面名報告
通常Firebaseは、AndroidならActivity遷移すると自動でActivityクラス名を拾ってscreen_view
イベントを送ってくれますが、Flutterの場合、Activityは1個なので、Flutter上でいくら遷移しても送信されません。
なので自前で送らなければならないのですが、かつては本当に色々やらないとダメだったような記事がある物の、最新版ではそんなに大変では無く入れられたので、まとめておきます。
なお、pubspec.yaml
への依存関係の設定は済んでいるものとします。
1.DebugViewを有効にしておく
まず、FirebaseのDebugViewが有効になるよう、設定をしておきます。
こちらを参考にして下さい。
Flutterに初めてのFirebase導入(Firebase Analytics)#接続が確認できないとき
2.AnalyticsServiceクラスを作成する
こんなクラスを作成しました。
多分、FirebaseAnalytics
のインスタンスは何個作っても大丈夫なようにFirebaseのライブラリ側がうまいことやっているはずですが、一応シングルトンを意識します。でもちゃんとしたシングルトンではありません。
/// アナリティクスイベント
enum AnalyticsEvent {
Button,
}
/// アナリティクス
class AnalyticsService {
static final FirebaseAnalytics analytics = FirebaseAnalytics();
static final FirebaseAnalyticsObserver observer =
FirebaseAnalyticsObserver(analytics: analytics);
/// ボタンタップイベント送信
Future<void> sendButtonEvent({@required String buttonName}) async {
sendEvent(
event: AnalyticsEvent.Button,
parameterMap: {'buttonName': "$buttonName"});
}
/// イベントを送信する
/// [event] AnalyticsEvent
/// [parameterMap] パラメータMap
Future<void> sendEvent(
{@required AnalyticsEvent event,
Map<String, dynamic> parameterMap}) async {
final eventName = event.toString().split('.')[1];
analytics.logEvent(name: eventName, parameters: parameterMap);
}
}
sendButtonEvent
で、ボタン名をパラメータとして送ります。
他に送りたいイベントが出来たら、AnalyticsEvent
に列挙値を追加していくイメージです。
sendEvent
関数だけでも十分そうですが、送りたいイベントのパターンって結構決まってくるイメージなので、ラッパー関数も用意しちゃっています。
final eventName = event.toString().split('.')[1];
ここですが、DartのenumをtoString
すると、Enumクラス名.列挙ID
となるので、後ろの名前だけ取っています。
3.get_itのlocatorに登録する
void setupLocator() {
locator.registerLazySingleton(() => NavigationService());
locator.registerLazySingleton(() => AnalyticsService());
}
NavigationService
というのは、こちらの記事で紹介したものです。
Flutter/Navigator遷移にBuildContextを不要にする/遷移のWidgetTest
setupLocator
関数は、main
関数で呼んでいます。
void main() async {
setupLocator();
...
runApp(...);
}
4.Navigatorにオブザーバーをセットする
class MyApp extends StatelessWidget {
MyApp({Key key}) : super();
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
...
// 追加ここから----------------
navigatorObservers: [
AnalyticsService.observer,
],
// 追加ここまで----------------
onGenerateRoute: (settings) {
...
},
initialRoute: "/",
// 画面
title: 'タイトル',
theme: ThemeData(
primarySwatch: Colors.blue,
),
);
}
}
5.NavigatorのRouteSettingsに画面名を入れる
return MaterialApp(
...
onGenerateRoute: (settings) {
switch (settings.name) {
case '/':
{
return MaterialPageRoute(
settings: RouteSettings(name: 'Topページ'), // これを追加
builder: (context) => TopPage());
}
case '/input':
final ProteinRecord record = settings.arguments;
return MaterialPageRoute(
settings: RouteSettings(name: 'Inputページ'), // これを追加
builder: (context) => InputPage(record: record));
default:
return MaterialPageRoute(
builder: (context) => Scaffold(
body: Center(
child: Text('No path for ${settings.name}'),
),
));
}
initialRoute: "/",
...
);
これで実行して、画面遷移をしてみて下さい。
TopPageから、IndexPageに遷移して、TopPageに戻る、と操作すると、DebugViewに、screen_view
のイベントが3回ほど届くはずです。
それぞれのイベントをクリックすると、パラメータ詳細が見られるので、firebase_screen
というところを開いてみると、セットした画面名が見られます。
他にもパラメータを見てみると、firebase_previous_screen
とかあるのが分かりますね。前にいた画面も一緒に送られるので、遷移が一方通行でない複雑なアプリでもある程度のことが取れそうです。
ボタンタップイベント
ボタンなどをタップしたのを送ります。
例えば、削除ボタンを押したとします。
FlatButton(
child: const Text("削除"),
onPressed: () => _onDeleteButton(context),
),
...
void _onDeleteButton(BuildContext context){
// Analytics送信
final analyticsService = locator<AnalyticsService>();
analyticsService.sendButtonEvent(buttonName: '削除ボタン');
// 削除処理
}
このイベントはDebugViewで次のように見えます。
Buttonイベントをクリックしてパラメータを見ると、Map{'buttonName': ボタン名}
で送ったカスタムパラメータ値がちゃんと届いているのが分かります。
Testの時送らないようにする
UnitTestやWidgetTestでは、送らないようにしましょう。
Mockitoを使えば簡単です。
https://pub.dev/packages/mockito
dev_dependencies:
flutter_test:
sdk: flutter
# mock
mockito: ^3.0.0
1.Mockクラス化
AnalyticsService
クラスのモッククラスを作ります。
/// MockAnalytics
class MockAnalyticsService extends Mock implements AnalyticsService {}
Flutterの言語であるDartには、暗黙的インターフェースという機能があり、implements AnalyticsService
とすることで、すべてのメソッドが未実装状態にしてくれます。
extends Mock
で、それらが何もしない関数ということにしてくれます(と、理解してます)。
2.テスト用のget_it初期化関数を作る
テスト用には、get_itパッケージがモック化したオブジェクトを返すようにします。
/// テスト用のget_it Locator初期化
void testSetupLocator({@required AnalyticsService analyticsService}) {
locator.registerSingleton(NavigationService());
locator.registerSingleton(analyticsService);
}
3.テストから使う
AnalyticsService
が必要な場所で、モック化クラスのオブジェクトを作って先ほどの初期化関数を呼び出します。
テストでは、イベント送信メソッドが呼ばれたかどうかチェックするのも良いですね。
group('画面遷移のテスト', () {
final mockAnalytics = MockAnalyticsService();
testSetupLocator(analyticsService: mockAnalytics);
testWidgets('削除ボタンタップ', (WidgetTester tester) async {
...
await tester.tap(find.byIcon(Icons.delete));
await tester.pumpAndSettle();
verify(mockAnalytics.sendButtonEvent(buttonName: '削除ボタン'));
});
});
Analyticsを上手く使うには
Analyticsの使いどころは、やはり一番はUserProperty
等と組み合わせてAudience
を作るときだと思います。有るイベントをたくさん送っているユーザーに対して、pushを送るとか、あるいは、特定のページを全然見てないユーザーにpushを送るとか、そういう戦略的なことが必要になってきたときに威力を発揮するかと思います。
とはいえ、じゃあ最初は全然取らなくて良いかというと、そうでもなくて。
以前調べたときは、Audience
のリストが出来るのは24Hくらいかかっていましたが、その上、更にAudience
作るのに必要な分析情報が送られてない!足さなきゃ!ってなると、アプリのリリースを踏まないと行けないため、かなり時間がかかってしまいます。
というわけで、最初から送れるものはある程度送っておいた方が都合が良いので、特に、お仕事で開発するアプリの場合は、この「アナリティクスに何をどう送っておくか?」も、結構重要な設計になってきます。
特に運営ユーザーが異なる場合には、この辺りもしっかり要件定義しておかないと、後からアナリティクスのイベント送信コードを入れればいいや〜と思っていると、とんでもない要件が上がってきて困ることになったりします。
FlutterならiOS/Android同じですが、別々に作ってたりすると、「パラメータが全然違う!合わない!」て事態にもなりがちです。
なので、ユーザーの動向を見て改良していこうというようなアプリの場合には、最初からアナリティクス送信イベントについてしっかり設計して積極的に収集しておくのが吉です。
参考サイトなど
【Dart/Flutter】Firebase AnalyticsのObserverとsetCurrentScreen()でページ遷移を監視する実装をまとめてみた
https://qiita.com/arthur_foreign/items/a019c490628323a96e4f
【Dart/Flutter】Firebase AnalyticsのlogEvent()を飛ばす実装についてまとめる
https://qiita.com/arthur_foreign/items/a6c2bc7ec661a847e5e6