A Google Analytics wrapper for command-line, web, and Flutter apps.

Related tags

Analytics usage
Overview

A wrapper around Google Analytics for command-line, web, and Flutter apps.

pub package Build Status Coverage Status

For web apps

To use this library as a web app, import the usage_html.dart library and instantiate the AnalyticsHtml class.

For Flutter apps

Flutter applications can use the AnalyticsIO version of this library. They will need to specify the documents directory in the constructor in order to tell the library where to save the analytics preferences:

import 'package:flutter/services.dart';
import 'package:usage/usage_io.dart';

void main() {
  final String UA = ...;

  Analytics ga = new AnalyticsIO(UA, 'ga_test', '3.0',
    documentsDirectory: PathProvider.getApplicationDocumentsDirectory());
  ...
}

For command-line apps

To use this library as a command-line app, import the usage_io.dart library and instantiate the AnalyticsIO class.

Note, for CLI apps, the usage library will send analytics pings asynchronously. This is useful it that it doesn't block the app generally. It does have one side-effect, in that outstanding asynchronous requests will block termination of the VM until that request finishes. So, for short-lived CLI tools, pinging Google Analytics can cause the tool to pause for several seconds before it terminates. This is often undesired - gathering analytics information shouldn't negatively effect the tool's UX.

One solution to this is to use the waitForLastPing({Duration timeout}) method on the analytics object. This will wait until all outstanding analytics requests have completed, or until the specified duration has elapsed. So, CLI apps can do something like:

await analytics.waitForLastPing(timeout: new Duration(milliseconds: 200));
analytics.close();

or:

await analytics.waitForLastPing(timeout: new Duration(milliseconds: 200));
exit(0);

Using the API

Import the package (in this example we use the dart:io version):

import 'package:usage/usage_io.dart';

And call some analytics code:

final String UA = ...;

Analytics ga = new AnalyticsIO(UA, 'ga_test', '3.0');
ga.analyticsOpt = AnalyticsOpt.optIn;

ga.sendScreenView('home');
ga.sendException('foo exception');

ga.sendScreenView('files');
ga.sendTiming('writeTime', 100);
ga.sendTiming('readTime', 20);

When do we send analytics data?

You can use this library in an opt-in manner or an opt-out one. It defaults to opt-out - data will be sent to Google Analytics unless the user explicitly opts-out. The mode can be adjusted by changing the value of the Analytics.analyticsOpt field.

Opt-out In opt-out mode, if the user does not explicitly opt-out of collecting analytics, the usage library will send usage data.

Opt-in In opt-in mode, no data will be sent until the user explicitly opt-in to collection. This includes screen views, events, timing information, and exceptions.

Other info

For both classes, you need to provide a Google Analytics tracking ID, the application name, and the application version.

Note: This library is intended for use with the Google Analytics application / mobile app style tracking IDs (as opposed to the web site style tracking IDs).

For more information, please see the Google Analytics Measurement Protocol Policy.

Contributing

Tests can be run using pub run test.

Comments
  • Huge output size

    Huge output size

    While regularly checking the output size (after dart2js) of your web application, I noticed a big increase. The uncompressed size of our javascript file increased by 400 kb. After compression using gzip it is still 100 kb bigger than before. This is not acceptable for us.

    I investigated what this increase is causing: It is the use of the uuid package and all it's dependencies. Currently uuid is used to generate the unique client id. Using uuid in console applications is fine. Looks like the normal Google Analytics implementation is only using a simple random number as a client id.

    I think it would be usefull if the generator for the client id could be set from the outside, or to force the use of a simple random number for the Html version of the library. The client id generator could be a simple callback function or a class as we already have for PostHandler and PersistentProperties.

    opened by Fox32 9
  • Allow batching of hits

    Allow batching of hits

    Making use of the /batch endpoint as described here: https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide#batch

    By default all events sent within the same sync-block (really until returning to the event-loop ) will be batched together. By changing the batchDelay in the constructor one can set a timeout after which all batched events are sent, or avoid the batching altogether.

    cla: yes 
    opened by sigurdm 8
  • does this work with web+app gtag js?

    does this work with web+app gtag js?

    I am trying to use this with the new web + app analytics by google, but i never receive events. is it possible that this is not possible with this library?

    opened by Freundschaft 7
  • allow for custom parameters when sending a screenView

    allow for custom parameters when sending a screenView

    Fixes https://github.com/dart-lang/usage/issues/112

    Wasn't sure what you wanted me to do. But here's a start.

    Also, not sure how to test this? I updated the test but it wasn't clear how to verify that it actually worked? The tests didn't fail.

    I didn't find a grind task for running tests or analyzing, so I ran the tests by hand.

    Thanks for taking a look!

    cla: yes 
    opened by sethladd 7
  • Used named optionals instead of positional

    Used named optionals instead of positional

    Many methods in Analytics use optional positionals. IMO optional positional should rarely be used on public APIs:

    • Callsite is less readable, especially for bools.
    • Hard to add more optional arguments in the future.
    • Have to pass null when you only care about the trailing optionals (which might overwrite the default value).

    Change to named arguments?

    opened by seaneagan 6
  • Allow a client to send a custom dimension

    Allow a client to send a custom dimension

    https://support.google.com/analytics/answer/2709828?hl=en

    We'd like to send custom dimensions for screens and events. Is there a way to do this with usage?

    Thanks!

    opened by sethladd 5
  • postEncode expect all values to be strings

    postEncode expect all values to be strings

    The sendTiming parameter time is of type int, but internally type string is required. Im using

          _analytics.sendTiming('LoadTime', stopwatch.elapsedMilliseconds);
    

    but it crashes as postEncode in usage_impl.dart requires all map values to be strings, but 'utt' is an integer. Therefore time should be converted to a string first.

    opened by Fox32 5
  • The tracking stops working after a burst of user interactions

    The tracking stops working after a burst of user interactions

    Interacting quickly with ui elements that trigger some tracking causes the package to stop working.

    This comes from the ThrottlingBucket.removeDrop() method that always returns false after a "successful" burst of requests, which prevents any subsequent attempt to send a payload to GA. The ThrottlingBucket class doesn't recover from this burst: the number of drops remains forever negative.

    The problem appeared while I was navigating rather fast in my app, but at a speed similar to a real life situation. And I am only tracking screen views at the moment, so this problem might occur more frequently when the app tracks screen views, events, exceptions, etc. and the stress on the throttling bucket increases.

    I have narrowed it down to the throttling bucket by adding a break point to the else part of the _sendPayload method:

    Future _sendPayload(String hitType, Map<String, dynamic> args) {
       if (!enabled) return Future.value();
    
       if (_bucket.removeDrop()) {
         _variableMap.forEach((key, value) {
           args[key] = value;
         });
    
         args['v'] = '1'; // protocol version
         args['tid'] = trackingId;
         args['cid'] = clientId;
         args['t'] = hitType;
    
         _sendController.add(args);
    
         return _recordFuture(postHandler.sendPost(_url, args));
       } else {
         return Future.value(); // <========= BREAK POINT HERE
       }
     }
    

    The app never stops there, until the burst of interaction succeeds in messing the throttling, then any subsequent attempt to track stops there, which I think proves the number of drops stays negative.

    opened by mr-mmmmore 4
  • Enable and fix package:pedantic lints

    Enable and fix package:pedantic lints

    • always_declare_return_types
    • omit_local_variable_types
    • prefer_collection_literals
    • prefer_if_null_operators
    • unawaited_futures

    Bump min SDK to 2.2.0 to support Set literals.

    cla: yes 
    opened by natebosch 4
  • Breaks on flutter master/alpha

    Breaks on flutter master/alpha

    E/flutter : [ERROR:dart_error.cc(17)] Unhandled exception:
    E/flutter : No top-level method 'getFilesDir' declared.
    E/flutter : 
    E/flutter : NoSuchMethodError: method not found: 'getFilesDir'
    E/flutter : Receiver: top-level
    E/flutter : Arguments: [...]
    E/flutter : #0      NoSuchMethodError._throwNew (dart:core-patch/errors_patch.dart:169)
    E/flutter : #1      createAnalytics.<createAnalytics_async_body> (package:usage/src/usage_impl_flutter.dart:22)
    E/flutter : #2      Future.Future.microtask.<anonymous closure> (dart:async/future.dart:144)
    E/flutter : #3      _microtaskLoop (dart:async/schedule_microtask.dart:41)
    E/flutter : #4      _startMicrotaskLoop (dart:async/schedule_microtask.dart:50)
    

    https://github.com/dart-lang/usage/blob/775bffd3a0c966e4dbc4e3e2c9fd6e29c079c054/lib/src/usage_impl_flutter.dart#L22

    getFilesDir was removed from flutter in https://github.com/flutter/flutter/pull/3636

    bug 
    opened by drewwarren 4
  • Handle runtime errors

    Handle runtime errors

    We have problems with errors from HttpRequest.request. There are several reasons that temporary error can occure, I our case it seems to be a Safari problem.

    For now that means that we have a unhandled exception that crashes the application. We can add manually a catchError to the returned futures from the Analytics class.

    Now I wonder if we should move handling such exceptions directly into the post handler?

    opened by Fox32 4
  • investigate supporting GA4

    investigate supporting GA4

    From the google analytics docs:

    all standard Universal Analytics properties will stop processing new hits on July 1, 2023

    We don't yet know whether we'll support GA4 in this package or leave that to another package. If we do support it here, it's enough of a protocol change that we likely wouldn't try for backwards compatibility in the APIs; i.e. v4 of this package and v5 would have different APIs (one that matched the needs of the older measurement protocol and one that matched the needs of GA4).

    This issue tracks discussing whether this package will be upgraded to support GA4 or not.

    --

    Notes:

    • deprecation notice: https://support.google.com/analytics/answer/11583528?hl=en
    • GA4 info: https://support.google.com/analytics/answer/10089681
    • GA4 protocol: https://developers.google.com/analytics/devguides/collection/protocol/ga4 and https://developers.google.com/analytics/devguides/collection/protocol/ga4/validating-events?client_type=gtag
    enhancement 
    opened by devoncarew 0
  • Support setting the clientId

    Support setting the clientId

    In order to support tracking a user across devices it would very handy to set the clientId manually. Support for this could be easily added by providing a setter for clientId in usage_impl.dart.

    opened by technolion 2
  • Allow analytics settings to be persisted elsewhere

    Allow analytics settings to be persisted elsewhere

    It looks like the settings are serialized to and from a file, however it would be nice to have control over where the data is being loaded and saved to (i.e. a data store, in memory, etc.). It would be helpful to have optional callbacks in the constructor that would handle saving and loading the serialized data, if omitted then file persistence would be required.

    enhancement 
    opened by jamiewest 0
  • Use XDG_CONFIG_HOME by default for IOPersistentProperties on macOS and linux

    Use XDG_CONFIG_HOME by default for IOPersistentProperties on macOS and linux

    Following the XDG Base Directory Specification makes this applications using this library better citizens on unixes.

    If the previously default file (named .$applicationName in the user's HOME directory) exists, or documentDirectory is specified, the behaviour is the same as before. In other cases, $XDG_CONFIG_HOME/applicationName/analytics is used.

    The code is pretty much the same as in my PR on the flutter repo and I submitted this PR to complete my mission of clearing flutter users' home directories of flutter files.

    cla: yes 
    opened by Jesse-Bakker 5
Owner
Dart
Dart is an open-source, scalable programming language, with robust libraries and runtimes, for building web, server, and mobile apps.
Dart
This plugin allows Flutter desktop apps to defines system tray.

tray_manager This plugin allows Flutter desktop apps to defines system tray. tray_manager Platform Support Quick Start Installation ⚠️ Linux requireme

LeanFlutter 122 Dec 22, 2022
Biyi (比译) is a convenient translation and dictionary app written in dart / Flutter.

Biyi (比译) is a convenient translation and dictionary app written in dart / Flutter.

biyidev 897 Jan 9, 2023
A Flutter package that makes navigation and routing easy.

A Flutter package that makes navigation and routing easy.

null 197 Jan 5, 2023
A sound meter app. Made with Flutter.

A sound meter app. Made with Flutter.

Fareez Iqmal 7 Dec 21, 2022
Flutter starter project - a template with best practices for starting a new app or becoming familiar with the architecture of our projects

Flutter starter project - a template with best practices for starting a new app or becoming familiar with the architecture of our projects

PatchAI 72 Nov 24, 2022
Instagram Clone made with Flutter

Instagram Clone made with Flutter

Hmida 22 Oct 14, 2022
A fitness app fetching data from Json built on flutter.

A fitness app fetching data from Json built on flutter.

null 0 Oct 5, 2021
Protofu is a Dart Command Line tool for generating your protobufs and included dependencies in one command.

ProtoFu - let me compile that for you ProtoFu exists to make working with protobufs easier. Gone are the days of downloading protoc and the dart proto

John McDole 5 Oct 27, 2022
A command-line application provide an load optimization solution for flutter web

一个命令行工具,针对flutter web加载慢和缓存问题提供了一套解决方案。 功能 通过大文件分片和资源文件cdn化方式,优化flutter web页面加载慢问题。 通过资源文件hash化,解决浏览器强缓存导致功能无法更新问题。 开始 局部安装 dev_dependencies: flutte

Barry 10 Dec 29, 2022
A Flutter example to use Google Maps in iOS and Android apps via the embedded Google Maps plugin Google Maps Plugin

maps_demo A Flutter example to use Google Maps in iOS and Android apps via the embedded Google Maps plugin Google Maps Plugin Getting Started Get an A

Gerry High 41 Feb 14, 2022
A simple command-line application to generate simple folder and file structure for Flutter Applications

Kanza_cli is a simple command line tool to generate folder and file structure for your Flutter apps. To use it, you should do the followings: 1. First

Kanan Yusubov 9 Dec 16, 2022
A simple shortcut, command line interface (CLI) for a lazy (a.k.a effective) Flutter developer in order to increase productivity and happiness.

f A simple shortcut, command line interface (CLI) for a lazy (a.k.a effective) Flutter developer in order to increase productivity and happiness. Inst

Salman S 27 Nov 22, 2022
Screenshots - A command line utility and package for capturing screenshots for Flutter

A screenshot image with overlaid status bar placed in a device frame. For an example of images generated with Screenshots on a live app in both stores

Maurice McCabe 258 Nov 22, 2022
Scaff is a simple command-line utility for generating Dart and Flutter components from template files.

Introduction Scaffold Generator for Dart and Flutter. scaff is a simple command-line utility for generating Dart and Flutter components from template

Ganesh Rathinavel Medayil 29 Jul 17, 2022
Listen to remote Flutter GTK application instances' command-line arguments and file open requests.

gtk_application This package allows the primary Flutter GTK application instance to listen to remote application instances' command-line arguments and

null 12 Dec 15, 2022
Uproot(uprt) is a multi-platform (Windows, MacOs, and Linux) command line utility written in Dart to convert a router's DHCP IP Reservations between routers

UPROOT Uproot(uprt) is a multi-platform (Windows, MacOs, and Linux) command line utility written in Dart to convert a router's DHCP IP Reservations be

GeekVisit 73 Jan 1, 2023
A tool to easily install the Android SDK command-line and platform tools.

gibadb A tool to easily install the Android SDK command-line and platform tools. For developers: This README describes the CLI tool that ships with th

null 3 Sep 22, 2022
changelog.dart provides a library and a command-line application to manage in the correct way the git metadata to build the changelog between two release

changelog.dart ?? changelog.dart: a collection of tools to manages in a fashion way a repository as maintainer. ?? Project Homepage Table of Content I

Vincenzo Palazzo 7 Dec 18, 2022
Package your Flutter app into OS-specific bundles (.app, .exe, etc.) via Dart or the command line.

flutter_app_packager Package your Flutter app into OS-specific bundles (.app, .exe, etc.) via Dart or the command line. The flutter_app_packager sourc

LeanFlutter 415 Jan 8, 2023
Package your Flutter app into OS-specific bundles (.dmg, .exe, etc.) via Dart or the command line.

flutter_distributor Package your Flutter app into OS-specific bundles (.dmg, .exe, etc.) via Dart or the command line. The flutter_distributor source

LeanFlutter 416 Dec 24, 2022