diff --git a/packages/plugins/plugin_firebase/lib/plugin_firebase.dart b/packages/plugins/plugin_firebase/lib/plugin_firebase.dart index 9f43c36..bb6f117 100644 --- a/packages/plugins/plugin_firebase/lib/plugin_firebase.dart +++ b/packages/plugins/plugin_firebase/lib/plugin_firebase.dart @@ -1,19 +1,15 @@ library analytics_plugin_firebase; +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions, Firebase; import 'package:segment_analytics/event.dart'; import 'package:segment_analytics/logger.dart'; -import 'package:segment_analytics/plugin.dart'; import 'package:segment_analytics/map_transform.dart'; - -import 'package:firebase_analytics/firebase_analytics.dart'; -import 'package:firebase_core/firebase_core.dart' - show FirebaseOptions, Firebase; - -export 'package:firebase_core/firebase_core.dart' - show FirebaseOptions, Firebase; - +import 'package:segment_analytics/plugin.dart'; import 'package:segment_analytics_plugin_firebase/properties.dart'; +export 'package:firebase_core/firebase_core.dart' show FirebaseOptions, Firebase; + class FirebaseDestination extends DestinationPlugin { final Future firebaseFuture; @@ -27,15 +23,26 @@ class FirebaseDestination extends DestinationPlugin { @override Future identify(IdentifyEvent event) async { + // Set user ID if provided if (event.userId != null) { - await FirebaseAnalytics.instance.setUserId(id: event.userId!); + await FirebaseAnalytics.instance.setUserId(id: event.userId); } + + // Set user properties from traits if provided if (event.traits != null) { - await Future.wait(event.traits!.toJson().entries.map((entry) async { - await FirebaseAnalytics.instance - .setUserProperty(name: entry.key, value: entry.value.toString()); - })); + // Transform and cast traits to the required format + final transformedTraits = recurseMapper(event.traits?.toJson(), mappings); + final userProperties = castParameterType(transformedTraits as Map); + + // Set each user property individually + for (final entry in userProperties.entries) { + await FirebaseAnalytics.instance.setUserProperty( + name: entry.key, + value: entry.value.toString(), + ); + } } + return event; } @@ -48,44 +55,37 @@ class FirebaseDestination extends DestinationPlugin { switch (event.event) { case 'Product Clicked': if (!(properties.containsKey('list_id') || - properties.containsKey('list_name') || + properties.containsKey('list_name') || properties.containsKey('name') || - properties.containsKey('itemId')) ) { + properties.containsKey('itemId'))) { throw Exception("Missing properties: list_name, list_id, name and itemID"); } - AnalyticsEventItem itemClicked = AnalyticsEventItem( - itemName: properties['name'].toString(), - itemId: properties['itemId'].toString()); + AnalyticsEventItem itemClicked = + AnalyticsEventItem(itemName: properties['name'].toString(), itemId: properties['itemId'].toString()); await FirebaseAnalytics.instance.logSelectItem( - itemListName: properties['list_name'].toString(), - itemListId: properties['list_id'].toString(), - items:[itemClicked], + itemListName: properties['list_name'].toString(), + itemListId: properties['list_id'].toString(), + items: [itemClicked], ); break; case 'Product Viewed': await FirebaseAnalytics.instance.logViewItem( currency: properties["currency"]?.toString(), - items: event.properties == null - ? null - : [AnalyticsEventItemJson(event.properties!)], + items: event.properties == null ? null : [AnalyticsEventItemJson(event.properties!)], value: double.tryParse(properties["value"].toString())); break; case 'Product Added': await FirebaseAnalytics.instance.logAddToCart( currency: properties["currency"]?.toString(), - items: event.properties == null - ? null - : [AnalyticsEventItemJson(event.properties!)], + items: event.properties == null ? null : [AnalyticsEventItemJson(event.properties!)], value: double.tryParse(properties["value"].toString())); break; case 'Product Removed': await FirebaseAnalytics.instance.logRemoveFromCart( currency: properties["currency"]?.toString(), - items: event.properties == null - ? null - : [AnalyticsEventItemJson(event.properties!)], + items: event.properties == null ? null : [AnalyticsEventItemJson(event.properties!)], value: double.tryParse(properties["value"].toString())); break; case 'Checkout Started': @@ -100,7 +100,7 @@ class FirebaseDestination extends DestinationPlugin { creativeName: properties["creativeName"]?.toString(), creativeSlot: properties["creativeSlot"]?.toString(), items: properties["items"] as List, - locationId: properties["locationdId"]?.toString(), + locationId: properties["locationId"]?.toString(), promotionId: properties["promotionId"]?.toString(), promotionName: properties["promotionName"]?.toString()); break; @@ -147,12 +147,10 @@ class FirebaseDestination extends DestinationPlugin { value: double.tryParse(properties["value"].toString())); break; case 'Cart Shared': - if (event.properties == null || - event.properties!['products'] == null) { + if (event.properties == null || event.properties!['products'] == null) { log("Error tracking event '${event.event}' for Firebase: products property must be a list of products"); } else if (event.properties!['products'] is List) { - await Future.wait( - (event.properties!['products'] as List).map((product) async { + await Future.wait((event.properties!['products'] as List).map((product) async { final productProperties = mapProperties(product, mappings); if (productProperties.containsKey("contentType") && productProperties.containsKey("itemId") && @@ -189,20 +187,18 @@ class FirebaseDestination extends DestinationPlugin { searchTerm: properties["searchTerm"].toString(), destination: properties["destination"]?.toString(), endDate: properties["endDate"]?.toString(), - numberOfNights: - int.tryParse(properties["numberOfNights"].toString()), - numberOfPassengers: - int.tryParse(properties["numberOfPassengers"].toString()), - numberOfRooms: - int.tryParse(properties["numberOfRooms"].toString()), + numberOfNights: int.tryParse(properties["numberOfNights"].toString()), + numberOfPassengers: int.tryParse(properties["numberOfPassengers"].toString()), + numberOfRooms: int.tryParse(properties["numberOfRooms"].toString()), origin: properties["origin"]?.toString(), startDate: properties["startDate"]?.toString(), travelClass: properties["travelClass"]?.toString()); break; default: await FirebaseAnalytics.instance.logEvent( - name: sanitizeEventName(event.event), - parameters: castParameterType(properties)); + name: sanitizeEventName(event.event), + parameters: castParameterType(properties), + ); break; } } catch (error) { @@ -213,8 +209,15 @@ class FirebaseDestination extends DestinationPlugin { @override Future screen(ScreenEvent event) async { - FirebaseAnalytics.instance - .logScreenView(screenClass: event.name, screenName: event.name); + // Transform and cast properties to the required format + final transformedProperties = recurseMapper(event.properties, mappings); + final parameters = castParameterType(transformedProperties as Map); + + FirebaseAnalytics.instance.logScreenView( + screenClass: event.name, + screenName: event.name, + parameters: parameters, + ); return event; } diff --git a/packages/plugins/plugin_firebase/lib/properties.dart b/packages/plugins/plugin_firebase/lib/properties.dart index 8f316d4..5415fc4 100644 --- a/packages/plugins/plugin_firebase/lib/properties.dart +++ b/packages/plugins/plugin_firebase/lib/properties.dart @@ -46,28 +46,42 @@ Map castParameterType(Map properties) { class AnalyticsEventItemJson extends AnalyticsEventItem { AnalyticsEventItemJson(Map json) : super( - affiliation: json['affiliation'].toString(), - currency: json['currency'].toString(), - coupon: json['coupon'].toString(), - creativeName: json['creativeName'].toString(), - creativeSlot: json['creativeSlot'].toString(), - discount: num.tryParse(json['discount'].toString()), - index: int.tryParse(json['index'].toString()), - itemBrand: json['itemBrand'].toString(), - itemCategory: json['itemCategory'].toString(), - itemCategory2: json['itemCategory2'].toString(), - itemCategory3: json['itemCategory3'].toString(), - itemCategory4: json['itemCategory4'].toString(), - itemCategory5: json['itemCategory5'].toString(), - itemId: json['itemId'].toString(), - itemListId: json['itemListId'].toString(), - itemListName: json['itemListName'].toString(), - itemName: json['itemName'].toString(), - itemVariant: json['itemVariant'].toString(), - locationId: json['locationId'].toString(), - price: num.tryParse(json['price'].toString()), - promotionId: json['promotionId'].toString(), - promotionName: json['promotionName'].toString(), - quantity: int.tryParse(json['quantity'].toString()), + affiliation: json['affiliation']?.toString(), + currency: json['currency']?.toString(), + coupon: json['coupon']?.toString(), + creativeName: json['creativeName']?.toString(), + creativeSlot: json['creativeSlot']?.toString(), + discount: _parseNum(json['discount']), + index: _parseInt(json['index']), + itemBrand: json['itemBrand']?.toString(), + itemCategory: json['itemCategory']?.toString(), + itemCategory2: json['itemCategory2']?.toString(), + itemCategory3: json['itemCategory3']?.toString(), + itemCategory4: json['itemCategory4']?.toString(), + itemCategory5: json['itemCategory5']?.toString(), + itemId: json['itemId']?.toString(), + itemListId: json['itemListId']?.toString(), + itemListName: json['itemListName']?.toString(), + itemName: json['itemName']?.toString(), + itemVariant: json['itemVariant']?.toString(), + locationId: json['locationId']?.toString(), + price: _parseNum(json['price']), + promotionId: json['promotionId']?.toString(), + promotionName: json['promotionName']?.toString(), + quantity: _parseInt(json['quantity']), ); + + // Helper to safely parse num values + static num? _parseNum(Object? value) { + if (value == null) return null; + if (value is num) return value; + return num.tryParse(value.toString()); + } + + // Helper to safely parse int values + static int? _parseInt(Object? value) { + if (value == null) return null; + if (value is int) return value; + return int.tryParse(value.toString()); + } } diff --git a/packages/plugins/plugin_firebase/pubspec.yaml b/packages/plugins/plugin_firebase/pubspec.yaml index 24c06b4..4c03364 100644 --- a/packages/plugins/plugin_firebase/pubspec.yaml +++ b/packages/plugins/plugin_firebase/pubspec.yaml @@ -1,6 +1,6 @@ name: segment_analytics_plugin_firebase description: The hassle-free way to add Segment analytics to your Flutter app. -version: 1.0.1 +version: 1.1.0 homepage: https://github.com/segmentio/analytics_flutter#readme repository: https://github.com/segmentio/analytics_flutter/tree/main/packages/plugins/plugin_firebase#readme issue_tracker: https://github.com/segmentio/analytics_flutter/issues @@ -10,15 +10,15 @@ environment: flutter: ">=3.16.0" dependencies: - firebase_analytics: ^11.3.3 - firebase_core: ^3.6.0 + firebase_analytics: ^12.0.4 + firebase_core: ^4.2.1 flutter: sdk: flutter - json_annotation: ^4.8.0 - segment_analytics: ^1.1.1 + json_annotation: ^4.9.0 + segment_analytics: ^1.1.10 dev_dependencies: - build_runner: ^2.3.3 + build_runner: ^2.10.3 flutter_test: sdk: flutter - flutter_lints: ^4.0.0 + flutter_lints: ^6.0.0