How to set Firebase Analytics custom events in Flutter without passing 'analytics/observer' object in each screen
Asked Answered
D

3

9

I'm setting up Firebase Analytics Package for my Flutter project. The sample provided in the library passes the analytics object for tracking events and observer for tracking the tab changes.

class MyApp extends StatelessWidget {
 ...
Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Firebase Analytics Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      navigatorObservers: <NavigatorObserver>[observer],
      home: MyHomePage(
        title: 'Firebase Analytics Demo',
        analytics: analytics,
        observer: observer,
      ),
    );
  }
}

...

class _MyHomePageState extends State<MyHomePage> {
...
Future<void> _sendAnalyticsEvent() async {
    await analytics.logEvent(
      name: 'test_event',
      parameters: <String, dynamic>{
        'string': 'string',
        'int': 42,
        'long': 12345678910,
        'double': 42.0,
        'bool': true,
      },
    );
    setMessage('logEvent succeeded');
  }
...
}

My app consists of a lot of screens where the state is managed via a Bloc package. Passing these objects from mainScreen all the way down the tree where the events are happening would not be good.

Is there a way to access them without passing them around the widget tree. Or would it work if I create a new object of the class FirebaseAnalytics and use the generated object to set an event?

static FirebaseAnalytics analytics = FirebaseAnalytics();

Or should I use Flutter_Bloc as a central place to log and set my Flutter Events?

Drews answered 12/7, 2019 at 9:10 Comment(0)
P
15

You can use the provider package to 'provide' both your analytics and observer down your widget tree.

class MyApp extends StatelessWidget {
  static FirebaseAnalytics analytics = FirebaseAnalytics();
  static FirebaseAnalyticsObserver observer =
      FirebaseAnalyticsObserver(analytics: analytics);

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
        providers: [
            Provider<FirebaseAnalytics>.value(value: analytics),
            Provider<FirebaseAnalyticsObserver>.value(value: observer),
        ],
        child: MaterialApp(
            title: 'Firebase Analytics Demo',
            theme: ThemeData(
                primarySwatch: Colors.blue,
            ),
            navigatorObservers: <NavigatorObserver>[observer],
            home: MyHomePage(
                title: 'Firebase Analytics Demo',
            ),
        ),
    ); 
  }
}

Then in any of your child widgets...

Future<void> _sendAnalyticsEvent() async {
    FirebaseAnalytics analytics = Provider.of<FirebaseAnalytics>(context);
    await analytics.logEvent(
      name: 'test_event',
      parameters: <String, dynamic>{
        'string': 'string',
        'int': 42,
        'long': 12345678910,
        'double': 42.0,
        'bool': true,
      },
    );
    setMessage('logEvent succeeded');
  }

Now you don't need to pass your Firebase instances to the widget constructors.

Purulence answered 12/7, 2019 at 9:33 Comment(1)
Using flutter_bloc would it matter if the MultiProvider() is parent or child of MultiBlocProvider() ?Clamber
B
9

Besides using provider or a global class with static parameters, another option is to use services with the GetIt package.

Just create a class that hold's the analytics and observer. E.g. like this:

class AnalyticsService {
  final FirebaseAnalytics _analytics = FirebaseAnalytics();

  FirebaseAnalyticsObserver getAnalyticsObserver() => FirebaseAnalyticsObserver(analytics: _analytics);

  Future logScreens({@required String name}) async {
    await _analytics.setCurrentScreen(screenName: name);
  }

  ... // implement the events you want to track
}

Now create a locator and register the class as a lazy singleton:

GetIt locator = GetIt.instance;

setupServiceLocator() {
  locator.registerLazySingleton<AnalyticsService>(() => AnalyticsService());
}

Make the locator accessable:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  setupServiceLocator();
  runApp(MyApp());
}

Now you can access the service from everywhere in your app by calling the locator. For example:

return MaterialApp(
  debugShowCheckedModeBanner: false,
  title: 'Demo',
  theme: themeData,
  initialRoute: SplashScreen.id,
  routes: routes,
  navigatorObservers: [
    // Access the observer
    locator<AnalyticsService>().getAnalyticsObserver(),
  ],
);

}

or

 onTap: () {
   locator<AnalyticsService>().logScreens(name: DetailsScreen.id);
 }
Bridesmaid answered 12/10, 2020 at 11:59 Comment(4)
hey, can you give me a demo project as you showed us, coz I don't know I want to trying to do that, but can't figure it out, it will be very helpful if you give us an example, Thank youNeolatin
Hey @AlwayssBijoy, here is a good explanation and the source of the above code: filledstacks.com/post/firebase-analytics-and-metrics-in-flutterBridesmaid
Thanks! Since I already have GetIt, this is exactly what I ended up doing.Unquestionable
I like getIt because I am using MultiBlocProvidor. And I am also using audio_service with getItCobbs
K
6

It is also possible to create a file with all of your global instances. e.g: file: globals.dart in your lib directory.

Content of the file:

class Global {
    static final FirebaseAnalytics analytics = FirebaseAnalytics();
}

Then to use it:

Global.analytics.logEvent(...)
Knop answered 9/4, 2020 at 18:25 Comment(2)
simplest and easiest. provider, getit, block all the rest are too much hocus pocusEverick
I am currently doing it exactly like this but the problem is that the screen/ event name is not defined. I'm getting the ones described by flutter like screen_view, session_start, first_open, etc.Vacation

© 2022 - 2024 — McMap. All rights reserved.