참고 자료
https://terry1213.github.io/flutter/flutter-firebase-analytics/#firebase_analytics에서-제공하는-표준-메소드-목록
https://velog.io/@ssorry_choi/Flutter-Firebase-Analytics
https://firebase.google.com/docs/analytics/
참고로, Firebase 설정 방법은 제외했습니다.
여기서는 순수하게 Flutter에서 GA(Google Analytics)를 다루는 방법에 대해서만 설명합니다.
main.dart
에서 MaterialApp()
에 navigatorObservers
파라미터를 설정해주어야 합니다.
class App extends StatelessWidget {
App({Key? key}) : super(key: key);
// 아래 줄 추가
final FirebaseAnalytics analytics = FirebaseAnalytics.instance;
Widget build(BuildContext context) {
return MaterialApp(
...
// 아래 줄 추가
navigatorObservers: [
FirebaseAnalyticsObserver(analytics: analytics),
],
);
}
}
GA에 로그를 남기는 메소드는 모두 log
라는 접두어로 시작합니다.
logAppOpen()
: 앱 실행logLogin({String loginMethod})
: 로그인logSignUp({String signUpMethod})
: 회원가입logSelectContent({String contentType, String itemId})
: 특정 카테고리(contentType)에서 어떤 아이템(itemId)을 선택했는지 알려줌.logTutorialBegin()
: 유저가 튜토리얼을 시작함.logTutorialComplete()
: 유저가 튜토리얼을 완료함.: 기본적으로 GA에서는 화면 전환을 자동으로 기록하고 있다…만, Flutter라서 그런지 몰라도 별도 설정을 안 하면 거의 대부분의 화면이 ‘Flutter’라는 이름으로 잡히는 문제가 있습니다. (즉, 어떤 화면이 잡힌 건지 알 수가 없는 문제가 있습니다.) 그래서 만약 화면별 기록이 필요하다면, 아래 메서드를 통해 명시적으로 화면의 표시 여부를 기록하는 게 나아 보입니다.
setCurrentScreen({String screenName, String screenClassOverride: ‘Flutter’})
: 해당 스크린이 나타날 때를 체크함.setUserId(String id)
: 현재 앱을 사용하는 유저의 아이디를 설정함.setUserId
를 설정한 이후에 발생하는 모든 이벤트에는 자동으로 유저 아이디가 붙기 때문에, 특정 유저가 발생시킨 모든 이벤트에 대해 추적할 수 있게 됩니다.setUserProperty({String name, String value})
: 현재 유저가 가지고 있는, name이라는 이름의 속성을 value로 설정함.logEvent({String name, Map<String, dynamic> parameters})
: 커스텀 이벤트 로그입니다.
How do I track Flutter screens in Firebase analytics?
위 글의 내용을 정리해보면 다음과 같습니다.
: 대부분의 Flutter 앱은 하나의 FlutterActivity(안드로이드) / FlutterAppDelegate(iOS)를 실행하고 그 안에서 화면을 랜더링합니다.
→ GA가 화면을 자동으로 추적하도록 하는 경우 FlutterActivity / FlutterAppDelegate만 잡히게 됩니다. (FlutterActivity / FlutterAppDelegate 안의 화면은 잡히지 않는다.)
→ 따라서 원하는 결과를 얻을 수 없습니다. 😞
💡 : 라우팅을 위해 RouteSettings
를 사용하지 않는 경우, GA로 PageView를 추적하기 위해선 추가적인 설정을 해 주어야 합니다!
FirebaseAnalyticsObserver
를 적용한다 해도 정상적인 PageView 추적이 불가능합니다.setCurrentScreen
메서드로 스크린 이름을 지정)만 적용한 결과, 실시간으로는 PageView가 잘 잡히는 것으로 보였으나 보고서에서는 PageView가 안 잡히는 현상이 있었습니다.: 아래 순서대로 하면 됩니다!
// Somewhere in your widgets...
FirebaseAnalytics().setCurrentScreen(screenName: '화면 이름');
(단, 위 코드만 지정해두는 경우 해당 스크린이 메모리상에 생성만 되어 있다면, 최상단 화면이 바뀔 때마다 계속 PageView가 발생한 것으로 간주되는 경우가 있습니다. 즉, 최상단 화면에 있지 않음에도 불구하고 PageView가 발생한 것으로 간주되면서 숫자를 잘못 세는 경우가 있기 때문에, 아래 작업까지 같이 해주어야 합니다.)
RouteAware
클래스를 구현한 mixin 구현체인 RouteAwareAnalytics
를 만들고, RouteAware
의 변화를 감지하는 객체인 routeObserver
도 만듭니다.RouteAwareAnalytics
는 앱의 라우팅을 관리하기 위한 것으로, 최상단 화면에 대해서만 PageView가 발생한 것으로 설정하고, 이를 GA에 보고하는 일을 합니다.import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter/widgets.dart';
// A Navigator observer that notifies RouteAwares of changes to state of their Route
final routeObserver = RouteObserver<PageRoute>();
mixin RouteAwareAnalytics<T extends StatefulWidget> on State<T> implements RouteAware {
AnalyticsRoute get route;
void didChangeDependencies() {
routeObserver.subscribe(this, ModalRoute.of(context));
super.didChangeDependencies();
}
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
void didPop() {}
void didPopNext() {
// Called when the top route has been popped off,
// and the current route shows up.
_setCurrentScreen(route);
}
void didPush() {
// Called when the current route has been pushed.
_setCurrentScreen(route);
}
void didPushNext() {}
Future<void> _setCurrentScreen(AnalyticsRoute analyticsRoute) {
print('Setting current screen to $analyticsRoute');
return FirebaseAnalytics().setCurrentScreen(
screenName: screenName(analyticsRoute),
screenClassOverride: screenClass(analyticsRoute),
);
}
}
routeObserver
를 MaterialApp에 등록합니다.FirebaseAnalyticsObserver
는 제거합니다.MaterialApp(
// ...
navigatorObservers: [
routeObserver,
// FirebaseAnalyticsObserver(analytics: FirebaseAnalytics())는 제거
],
);
RouteAwareAnalytics
를 추가하고, route
를 지정해줍니다.class ExampleRoute extends StatefulWidget {
_ExampleRouteState createState() => _ExampleRouteState();
}
// state 혹은 controller에 RouteAwareAnalytics 추가
class _ExampleRouteState extends State<ExampleRoute> with RouteAwareAnalytics {
Widget build(BuildContext context) => Text('Example');
// route를 지정함
// GA에선 이 화면에 대한 이름을 route로 인식합니다.
// 따라서 이 화면에 대한 PageView를 확인할 때,
// 아래의 route에서 지정한 이름으로 PageView를 찾으면 됩니다.
AnalyticsRoute get route => AnalyticsRoute.example;
}
추가)
5번 과정을 모든 화면에 대해 해야 한다면, 모든 화면에 일일이 위 코드를 적용하는 것은 낭비라고 생각합니다. 따라서, 만약 노가다를 줄이고 싶다면 BaseState / BaseController와 같은 공통 state / controller를 만들어서, 거기에 위 코드를 넣는 것을 추천합니다.
특히, 이를 binding하고 묶어서 사용하면 훨씬 효율적인 코드를 작성할 수 있습니다. (이 부분에 대해선 추후 새 글로 추가하겠습니다.)
안녕하세요 글 잘 읽었습니다.
궁금한 점이 있어서 문의드립니다.
screenClassOverride: screenClass(analyticsRoute)
에서screenClass()
는 어떻게 구현하신 하신건가요??