Created
October 25, 2023 12:21
-
-
Save hawkkiller/515aca8c55e29d343375281fd58cd153 to your computer and use it in GitHub Desktop.
Analytics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:analytics_example/analytics_property.dart'; | |
/// A builder for analytics properties. | |
/// | |
/// This class is used to build a map of properties | |
/// that can be sent to an analytics service. | |
final class AnalyticsBuilder { | |
AnalyticsBuilder() : properties = []; | |
/// The properties that have been added to this builder. | |
final List<AnalyticsProperty> properties; | |
/// Adds a property to this builder. | |
void add(AnalyticsProperty property) => properties.add(property); | |
/// Returns the properties as a map. | |
/// | |
/// This method should be called after all properties have been added. | |
Map<String, Object?> toMap() { | |
final result = <String, Object?>{}; | |
for (final property in properties) { | |
result[property.name] = property.valueSerializable; | |
} | |
return result; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:analytics_example/analytics_builder.dart'; | |
import 'package:analytics_example/analytics_property.dart'; | |
abstract base class AnalyticsEvent { | |
const AnalyticsEvent(); | |
String get name; | |
/// Builds the properties for this event. | |
void buildProperties(AnalyticsBuilder builder) {} | |
} | |
final class UserLoggedEvent extends AnalyticsEvent { | |
const UserLoggedEvent({ | |
required this.userId, | |
required this.paid, | |
}); | |
final String userId; | |
final bool paid; | |
@override | |
String get name => 'user_logged'; | |
@override | |
void buildProperties(AnalyticsBuilder builder) { | |
builder.add(StringAnalyticsProperty('user_id', userId)); | |
builder.add(FlagAnalyticsProperty('paid', paid)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/foundation.dart'; | |
sealed class AnalyticsProperty<T extends Object> { | |
const AnalyticsProperty(this.name, this.value, {this.meta}); | |
/// The name of this property. | |
final String name; | |
/// The value of this property. | |
final T? value; | |
/// Additional information about this property. | |
/// | |
/// This can be used to provide additional context about the property. | |
final Object? meta; | |
/// Returns the value of this property in a form that can be serialized to JSON. | |
/// | |
/// If the value is not serializable, this field should be | |
/// overridden to return a serializable value. | |
Object? get valueSerializable => value; | |
} | |
final class IntAnalyticsProperty extends AnalyticsProperty<int> { | |
IntAnalyticsProperty(super.name, super.value, {super.meta}); | |
} | |
final class DoubleAnalyticsProperty extends AnalyticsProperty<double> { | |
DoubleAnalyticsProperty(super.name, super.value, {super.meta}); | |
} | |
final class StringAnalyticsProperty extends AnalyticsProperty<String> { | |
StringAnalyticsProperty(super.name, super.value, {super.meta}); | |
} | |
final class FlagAnalyticsProperty extends AnalyticsProperty<bool> { | |
FlagAnalyticsProperty(super.name, super.value, {super.meta}); | |
@override | |
Object? get valueSerializable { | |
if (value == null) return null; | |
return value! ? 'true' : 'false'; | |
} | |
} | |
final class EnumAnalyticsProperty<T extends Object> extends AnalyticsProperty<T> { | |
EnumAnalyticsProperty(super.name, super.value, {super.meta}); | |
@override | |
Object? get valueSerializable { | |
if (value == null) return null; | |
return describeEnum(value!); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'package:analytics_example/analytics_builder.dart'; | |
import 'package:analytics_example/analytics_event.dart'; | |
import 'package:analytics_example/analytics_property.dart'; | |
import 'package:firebase_analytics/firebase_analytics.dart'; | |
import 'package:flutter/foundation.dart'; | |
abstract base class AnalyticsInterceptor { | |
const AnalyticsInterceptor(); | |
/// Intercepts an event before it is reported to the analytics service | |
FutureOr<void> afterReport( | |
AnalyticsEvent event, | |
List<AnalyticsProperty> properties, | |
) {} | |
} | |
final class LoggingAnalyticsInterceptor extends AnalyticsInterceptor { | |
const LoggingAnalyticsInterceptor(); | |
@override | |
FutureOr<void> afterReport( | |
AnalyticsEvent event, | |
List<AnalyticsProperty> properties, | |
) { | |
if (kDebugMode) { | |
print('Event: ${event.name}'); | |
for (final property in properties) { | |
print(' ${property.name}: ${property.value}'); | |
} | |
} | |
} | |
} | |
abstract base class AnalyticsReporter { | |
/// Creates a new analytics reporter. | |
const AnalyticsReporter({List<AnalyticsInterceptor> interceptors = const []}) | |
: _interceptors = interceptors; | |
/// The interceptors that will be called before an event is reported. | |
final List<AnalyticsInterceptor> _interceptors; | |
/// Reports an event to the analytics service. | |
@nonVirtual | |
Future<void> reportEvent(AnalyticsEvent event) async { | |
final builder = AnalyticsBuilder(); | |
event.buildProperties(builder); | |
await _reportEvent(event.name, builder.toMap()); | |
for (final interceptor in _interceptors) { | |
await interceptor.afterReport(event, builder.properties); | |
} | |
} | |
/// Reports an event to the analytics service. | |
/// | |
/// This method should be implemented by the analytics service. | |
/// | |
/// For example, if you are using Firebase, you would implement this method | |
/// to call `FirebaseAnalytics.logEvent`. | |
Future<void> _reportEvent(String name, Map<String, Object?> properties); | |
} | |
final class FirebaseAnalyticsReporter extends AnalyticsReporter { | |
const FirebaseAnalyticsReporter(this._service, {super.interceptors}); | |
final FirebaseAnalytics _service; | |
@override | |
Future<void> _reportEvent(String name, Map<String, Object?> properties) => | |
_service.logEvent( | |
name: name, | |
parameters: properties, | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment