brdiscovery 0.5.0 copy "brdiscovery: ^0.5.0" to clipboard
brdiscovery: ^0.5.0 copied to clipboard

Bloomreach Discovery - API SDK for executing autosuggest, product, category and visual search within a Flutter application

Bloomreach Discovery SDK for Flutter/Dart #

pub package style: very good analysis License: MIT

A comprehensive Flutter/Dart SDK for Bloomreach Discovery APIs, providing seamless integration for product search, category browsing, autosuggest, visual search, recommendations, and pixel tracking capabilities in your e-commerce applications.

Features #

🔍 Product Search - Powerful keyword-based product discovery
📂 Category Search - Browse products by categories
🤖 Autosuggest - Real-time search suggestions and autocomplete
👁️ Visual Search - AI-powered image-based product search
🎯 Recommendations & Pathways - Complete widget-based recommendation system
📊 Advanced Filtering - Faceted search with ranges and grouping
🎪 Personalization - User-specific recommendations and targeting
🏪 BOPIS Support - Buy Online, Pick-up In Store functionality 📈 Pixel Tracking - Comprehensive user behavior tracking and analytics

Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  brdiscovery: ^0.5.0

Then run:

flutter pub get

Or install it from the command line:

flutter pub add brdiscovery

Quick Start #

1. Import the Package #

import 'package:brdiscovery/api.dart';

2. Initialize API Clients #

// Product Search
final productSearchApi = ProductSearchApi();

// Autosuggest
final autoSuggestApi = AutoSuggestApi();

// Visual Search
final visualSearchApi = VisualSearchApi();
final visualUploadApi = VisualUploadApi();

// Recommendations & Pathways
final recommendationsApi = RecommendationsApi();

3. Initialize Pixel Tracking (New!) #

// Initialize pixel tracker for analytics
final brPixel = BrPixel(
  accountId: 'your_account_id',
  uuid: 'your_device_uuid', // Android Advertising ID or similar
  visitorType: VisitorType.newUser, // or VisitorType.returningUser
  baseUrl: 'https://yourapp.com/',
  domainKey: 'your_domain_key',
  userId: 'user123', // Optional
  debugMode: true, // Set to false in production
  currency: 'USD',
  pixelUrlByRegion: PixelRegion.na.url,
);

PixelTracker.init(brPixel);

4. Basic Product Search with Tracking #

try {
  // Perform product search
  final response = await productSearchApi.searchProducts(
    'your_br_uid_2',        // Browser tracking ID
    12345,                  // Account ID
    'your_domain_key',      // Domain key
    'search query',         // Search term
    rows: 20,              // Results per page
    start: 0,              // Starting offset
  );
  
  // Track the search event
  PixelTracker.searchEventPixel(
    'https://yourapp.com/home',
    'Search Results',
    'search query',
  );
  
  if (response?.response?.docs != null) {
    for (final product in response!.response!.docs) {
      print('Product: ${product.title} - \${product.price}');
    }
  }
} catch (e) {
  print('Error: $e');
}

5. Basic Recommendations with Tracking #

try {
  // Get keyword-based recommendations
  final recommendations = await recommendationsApi.getKeywordRecommendations(
    'widget_id',            // Widget ID from dashboard
    12345,                  // Account ID
    'your_br_uid_2',        // Browser tracking ID
    'your_domain_key',      // Domain key
    'laptops',              // Query for recommendations
    'request_id_123',       // Request ID for tracking
    'https://yoursite.com', // Current page URL
    'https://yoursite.com', // Referrer URL
    facet: true,            // Enable facets
    rows: 10,              // Number of recommendations
  );
  
  // Track widget view
  PixelTracker.widgetView(
    'request_id_123',
    'laptops',
    'widget_id',
    'keyword'
  );
  
  if (recommendations?.response?.docs != null) {
    for (final product in recommendations!.response!.docs) {
      print('Recommended: ${product.title} - \${product.price}');
    }
  }
} catch (e) {
  print('Error: $e');
}

Pixel Tracking Usage #

Page View Tracking #

// Track different page types
PixelTracker.homePageViewPixel(
  'https://yourapp.com/previous',
  'Home Screen'
);

PixelTracker.productPageViewPixel(
  'https://yourapp.com/category',
  'Product Details',
  'product123',
  'Amazing Product',
  sku: 'SKU123',
);

PixelTracker.categoryPageViewPixel(
  'https://yourapp.com/home',
  'Electronics Category',
  'electronics',
  'Home|Electronics'
);

PixelTracker.searchResultPageViewPixel(
  'https://yourapp.com/home',
  'Search Results',
  'laptop'
);

Event Tracking #

// Track user interactions
PixelTracker.addToCartEventPixel(
  'https://yourapp.com/product/123',
  'Product Details',
  'product123',
  'Amazing Product',
  'SKU123',
);

PixelTracker.searchEventPixel(
  'https://yourapp.com/search',
  'Search Page',
  'laptop computers',
);

PixelTracker.suggestionEventPixel(
  'https://yourapp.com/search',
  'Search Page',
  'lap', // What user typed
  'laptop', // What they clicked
);

Widget Tracking #

// Track recommendation widget interactions
PixelTracker.widgetView(
  'response_id_123',
  'search_query',
  'widget123',
  'recommendation'
);

PixelTracker.widgetClick(
  'response_id_123',
  'search_query',
  'widget123',
  'recommendation',
  'product456'
);

PixelTracker.widgetAddToCart(
  'response_id_123',
  'widget123',
  'search_query',
  'recommendation',
  'product456',
  widgetAtcDataSku: 'SKU456',
);

Conversion Tracking #

// Track successful purchases
PixelTracker.conversionPageView(
  'https://yourapp.com/cart',
  'Order Complete',
  true,
  '299.99',
  'order456',
  [
    PixelBasketItem(
      prodId: 'product123',
      name: 'Amazing Product',
      sku: 'SKU123',
      quantity: '1',
      price: '299.99',
    ),
  ]
);

Usage Examples #

Recommendations & Pathways #

// Keyword-based recommendations with faceting
final keywordRecs = await recommendationsApi.getKeywordRecommendations(
  'widget_id_123',
  accountId,
  brUid2,
  domainKey,
  'wireless headphones',   // Search query
  'request_id_456',
  'https://yoursite.com',
  'https://yoursite.com',
  facet: true,            // Enable facets for filtering
  rows: 20,
  filter: 'category:"Electronics"',
  userId: 'user123',
);

// Item-based "More like this" recommendations
final itemRecs = await recommendationsApi.getItemBasedRecommendations(
  'pdp_widget_id',
  accountId,
  brUid2,
  domainKey,
  'product_id_789',       // Product ID for similar items
  'request_id_789',
  'https://yoursite.com',
  'https://yoursite.com',
  rows: 8,
  contextPids: 'product_id_789', // Context for merchandising
  userId: 'user123',
);

// Category browsing with recommendations
final categoryRecs = await recommendationsApi.getCategoryRecommendations(
  'category_widget_id',
  accountId,
  brUid2,
  domainKey,
  'electronics_cat_id',   // Category ID
  'request_id_101',
  'https://yoursite.com',
  'https://yoursite.com',
  facet: true,
  rows: 24,
  userId: 'user123',
);

// Personalized recommendations for logged-in users
final personalRecs = await recommendationsApi.getPersonalizedRecommendations(
  'personal_widget_id',
  accountId,
  brUid2,
  domainKey,
  'recommended for you',  // Query for personalization
  'user123',              // Required user ID
  'request_id_202',
  'https://yoursite.com',
  'https://yoursite.com',
  rows: 15,
);

// Global trending/popular products
final globalRecs = await recommendationsApi.getGlobalRecommendations(
  'trending_widget_id',
  accountId,
  brUid2,
  domainKey,
  'request_id_303',
  'https://yoursite.com',
  'https://yoursite.com',
  rows: 12,
  filter: 'is_live:true',
);

// Recently viewed products
final recentRecs = await recommendationsApi.getRecentlyViewedRecommendations(
  'recent_widget_id',
  accountId,
  brUid2,
  domainKey,
  'request_id_404',
  'https://yoursite.com',
  'https://yoursite.com',
  userId: 'user123',
  rows: 10,
);

Product Search with Advanced Features #

final response = await productSearchApi.getProductSearchResults(
  brUid2,
  accountId,
  domainKey,
  'laptop',                    // Search query
  SearchType.KEYWORD,
  0,                          // Start index
  20,                         // Number of results
  'https://yoursite.com',     // Current page URL
  'https://yoursite.com',     // Referrer URL
  sort: 'price asc',          // Sort by price ascending
  fq: 'brand:"Apple"',        // Filter by brand
  facetVersion: 3.0,          // Use modern facet format
  ll: '37.7749,-122.4194',    // Location for BOPIS
  userId: 'user123',          // User identifier
);

// Access products
final products = response?.response?.docs ?? [];

// Access facets for filtering UI
final facets = response?.facetCounts?.facets ?? [];

// Check for grouped results
if (response?.groupResponse != null) {
  final groups = response!.groupResponse!.groups;
  // Handle grouped product results
}

Category Browsing #

final categoryResponse = await productSearchApi.searchCategory(
  brUid2,
  accountId,
  domainKey,
  'electronics',              // Category ID
  rows: 20,
  start: 0,
  sort: 'popularity desc',
  fq: 'price:[50 TO 500]',    // Price range filter
);

Autosuggest Implementation #

final suggestions = await autoSuggestApi.getAutoSuggest(
  accountId,
  brUid2,
  'product:default',          // Catalog views
  'lap',                      // Partial query
  'https://yoursite.com',     // Current URL
  'https://yoursite.com',     // Referrer URL
  requestType: 'suggest',
);

// Process suggestions
if (suggestions?.suggestionGroups != null) {
  for (final group in suggestions!.suggestionGroups) {
    // Query suggestions (search terms)
    for (final querySuggestion in group.querySuggestions) {
      print('Suggested query: ${querySuggestion.displayText}');
      
      // Track suggestion click when user selects
      PixelTracker.suggestionEventPixel(
        'https://yoursite.com/search',
        'Search Page',
        'lap', // what user typed
        querySuggestion.query ?? '', // what they clicked
      );
    }
    
    // Product suggestions
    for (final productSuggestion in group.searchSuggestions) {
      print('Suggested product: ${productSuggestion.title}');
    }
    
    // Attribute suggestions (brands, categories, etc.)
    for (final attrSuggestion in group.attributeSuggestions) {
      print('Suggested ${attrSuggestion.attributeType}: ${attrSuggestion.value}');
    }
  }
}
// 1. Upload an image
final imageFile = await MultipartFile.fromPath(
  'image',
  '/path/to/image.jpg',
);

final uploadResponse = await visualUploadApi.uploadAPI(
  'widget_id',
  accountId,
  domainKey,
  image: imageFile,
);

// 2. Search using the uploaded image
if (uploadResponse?.response?.imageId != null) {
  final visualResults = await visualSearchApi.getProductSearchResults(
    'widget_id',
    accountId,
    brUid2,
    domainKey,
    uploadResponse!.response!.imageId!,
    'https://yoursite.com',
    rows: 10,
    filter: 'price:[0 TO 1000]',  // Optional filters
  );
  
  // Process visual search results
  final products = visualResults?.response?.docs ?? [];
}

Working with Dynamic Product Fields #

The SDK uses a flexible SearchResponseDoc model that adapts to your product catalog:

final doc = response.response!.docs.first;

// Access common fields with convenience getters
print('ID: ${doc.pid}');
print('Title: ${doc.title}');
print('Price: ${doc.price}');
print('Sale Price: ${doc.salePrice}');
print('Brand: ${doc.brand}');

// Access any custom field dynamically
final customValue = doc.getField<String>('custom_attribute');
final numericValue = doc.getField<double>('rating');

// Get field with default value
final description = doc.getFieldWithDefault<String>('description', 'No description');

// Check if field exists
if (doc.hasField('inventory_count')) {
  final inventory = doc.getField<int>('inventory_count');
}

// Get all available fields
final availableFields = doc.availableFields;
print('Available fields: $availableFields');

Error Handling #

try {
  final response = await productSearchApi.searchProducts(
    brUid2,
    accountId,
    domainKey,
    'search term',
  );
  
  // Handle successful response
  if (response?.response?.numFound == 0) {
    print('No products found');
  }
} on ApiException catch (e) {
  print('API Error: ${e.code} - ${e.message}');
  // Handle specific API errors
} catch (e) {
  print('General error: $e');
  // Handle network or other errors
}

Configuration #

Environment Setup #

// Production environment (default)
final productSearchApi = ProductSearchApi(PscApiClient(env: Environment.PRODUCTION));
final recommendationsApi = RecommendationsApi(RecommendationsApiClient(env: Environment.PRODUCTION));

// Staging environment
final productSearchApi = ProductSearchApi(PscApiClient(env: Environment.STAGING));
final recommendationsApi = RecommendationsApi(RecommendationsApiClient(env: Environment.STAGING));

Custom Headers #

final pscApiClient = PscApiClient();
pscApiClient.addDefaultHeader('Custom-Header', 'Value');
final productSearchApi = ProductSearchApi(pscApiClient);

final recsApiClient = RecommendationsApiClient();
recsApiClient.addDefaultHeader('Custom-Header', 'Value');
final recommendationsApi = RecommendationsApi(recsApiClient);

Pixel Tracking Configuration #

// Configure pixel tracker with all options
final brPixel = BrPixel(
  accountId: 'your_account_id',
  uuid: 'your_device_uuid',
  visitorType: VisitorType.returningUser,
  baseUrl: 'https://yourapp.com/',
  domainKey: 'your_domain_key',
  userId: 'user123',
  testData: false, // Set to true for testing
  debugMode: false, // Set to true for development
  currency: 'USD',
  pixelUrlByRegion: PixelRegion.na.url, // or PixelRegion.eu.url
  customerTier: 'premium',
  customerCountry: 'US',
  customerGeo: 'NA',
  customerProfile: 'mobile_user',
  viewId: 'mobile_view',
  abTest: 'variant_a',
);

API Reference #

Product Search API #

Method Description
getProductSearchResults() Full product search with all parameters
searchProducts() Convenient method for keyword search
searchCategory() Convenient method for category browsing

Recommendations API #

Method Description
getKeywordRecommendations() Search-driven recommendations with faceting
getItemBasedRecommendations() "More like this" and similar product recommendations
getCategoryRecommendations() Category-based browsing with filtering
getPersonalizedRecommendations() User-specific personalized recommendations
getGlobalRecommendations() Site-wide trending and popular products
getRecentlyViewedRecommendations() Recently viewed products for users

Autosuggest API #

Method Description
getAutoSuggest() Get search suggestions and product recommendations

Visual Search API #

Method Description
uploadAPI() Upload image for visual search
getProductSearchResults() Search products using uploaded image

Pixel Tracker API #

Method Description
init() Initialize pixel tracker with configuration
homePageViewPixel() Track home page views
productPageViewPixel() Track product page views
categoryPageViewPixel() Track category page views
searchResultPageViewPixel() Track search result page views
conversionPageView() Track conversion/thank you pages
addToCartEventPixel() Track add to cart events
searchEventPixel() Track search events
suggestionEventPixel() Track autosuggest clicks
quickViewEventPixel() Track quick view events
widgetView() Track widget view events
widgetClick() Track widget click events
widgetAddToCart() Track widget add to cart events
customPageViewPixel() Track custom page views
customEventPixel() Track custom events

Key Parameters #

  • brUid2: Browser tracking cookie for analytics
  • accountId: Your Bloomreach account identifier
  • domainKey: Site domain identifier
  • widgetId: Widget ID from Dashboard Widget Configurator (for recommendations)
  • fl: Field list - comma-separated fields to return
  • fq: Filter query for faceted search
  • sort: Sort order (e.g., "price asc", "popularity desc")
  • facetVersion: Use 3.0 for modern unified facet format
  • groupby: Group results by field (e.g., "brand", "category")
  • userId: User identifier for personalization
  • contextPids: Context product IDs for enhanced recommendations

Advanced Features #

Widget-Based Recommendations #

The SDK supports all major recommendation widget types for building complete recommendation systems:

// Homepage trending products
final trending = await recommendationsApi.getGlobalRecommendations(
  'homepage_widget', accountId, brUid2, domainKey, requestId, url, url,
  rows: 12,
);

// Product detail page "More like this"
final similar = await recommendationsApi.getItemBasedRecommendations(
  'pdp_widget', accountId, brUid2, domainKey, currentProductId, requestId, url, url,
  rows: 8, contextPids: currentProductId,
);

// Search results with recommendations
final searchRecs = await recommendationsApi.getKeywordRecommendations(
  'search_widget', accountId, brUid2, domainKey, searchQuery, requestId, url, url,
  facet: true, rows: 20, userId: userId,
);

// Personalized recommendations for logged-in users
final personal = await recommendationsApi.getPersonalizedRecommendations(
  'personal_widget', accountId, brUid2, domainKey, 'for you', userId, requestId, url, url,
  rows: 15,
);
// Using unified facet format (v3.0)
final response = await productSearchApi.searchProducts(
  brUid2, accountId, domainKey, 'query',
  facetVersion: 3.0,
);

// Process facets for filter UI
if (response?.facetCounts?.facets != null) {
  for (final facet in response!.facetCounts!.facets!) {
    print('Facet: ${facet.name} (${facet.type})');
    
    for (final value in facet.value) {
      if (value.name != null) {
        // Text facet (brand, category, etc.)
        print('  ${value.name}: ${value.count} products');
      } else if (value.start != null && value.end != null) {
        // Range facet (price, rating, etc.)
        print('  ${value.start}-${value.end}: ${value.count} products');
      }
    }
  }
}

Product Grouping #

final response = await productSearchApi.getProductSearchResults(
  brUid2, accountId, domainKey, 'query',
  SearchType.KEYWORD, 0, 20, url, refUrl,
  groupby: 'brand',        // Group by brand
  groupLimit: 5,           // Max 5 products per group
);

// Access grouped results
if (response?.groupResponse != null) {
  response!.groupResponse!.groups.forEach((groupKey, groupList) {
    print('Brand: $groupKey (${groupList.matches} total matches)');
    
    for (final group in groupList.groups) {
      print('  Group value: ${group.groupValue}');
      print('  Products in group: ${group.doclist?.docs.length}');
    }
  });
}

BOPIS (Buy Online, Pick-up In Store) #

final response = await productSearchApi.searchProducts(
  brUid2, accountId, domainKey, 'query',
  ll: '37.7749,-122.4194',  // Latitude,longitude
  fq: 'store_inventory:true',
);

Complete User Journey with Tracking #

// Example: Complete e-commerce user journey with pixel tracking
class EcommerceJourney {
  final ProductSearchApi productSearchApi;
  final RecommendationsApi recommendationsApi;
  
  EcommerceJourney(this.productSearchApi, this.recommendationsApi);
  
  Future<void> simulateUserJourney() async {
    // 1. User lands on home page
    PixelTracker.homePageViewPixel(
      'https://app.com/external',
      'Home Page'
    );
    
    // 2. User searches for products
    final searchResponse = await productSearchApi.searchProducts(
      brUid2, accountId, domainKey, 'laptop',
    );
    
    PixelTracker.searchEventPixel(
      'https://app.com/home',
      'Search Results',
      'laptop'
    );
    
    // 3. User views product
    final product = searchResponse?.response?.docs?.first;
    if (product != null) {
      PixelTracker.productPageViewPixel(
        'https://app.com/search',
        'Product Details',
        product.pid ?? '',
        product.title ?? '',
        sku: product.getField<String>('sku'),
      );
      
      // 4. User adds to cart
      PixelTracker.addToCartEventPixel(
        'https://app.com/product/${product.pid}',
        'Product Details',
        product.pid ?? '',
        product.title ?? '',
        product.getField<String>('sku') ?? '',
      );
      
      // 5. User completes purchase
      PixelTracker.conversionPageView(
        'https://app.com/cart',
        'Order Complete',
        true,
        '999.99',
        'order_123',
        [
          PixelBasketItem(
            prodId: product.pid ?? '',
            name: product.title ?? '',
            sku: product.getField<String>('sku'),
            quantity: '1',
            price: '999.99',
          ),
        ],
      );
    }
  }
}

Testing #

The package includes comprehensive tests. Run them with:

flutter test

Example test files:

  • test/product_search_api_test.dart - Basic product search tests
  • test/autosuggest_api_test.dart - Autosuggest functionality tests
  • test/visual_search_api_test.dart - Visual search tests
  • test/pixel_tracker_test.dart - Pixel tracking tests
  • test/recommendations_test.dart - Recommendations API tests

Requirements #

  • Dart: >=2.12.0 <4.0.0
  • Flutter: >=2.0.0

Dependencies #

  • collection: ^1.17.0
  • http: ^1.1.0
  • intl: ^0.20.2
  • meta: ^1.1.8

Contributing #

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Changelog #

0.5.0 #

  • NEW: Pixel Tracking - Complete user behavior tracking and analytics
    • Page view tracking (home, product, category, search, content, conversion)
    • Event tracking (add to cart, search, suggestions, quick view)
    • Widget interaction tracking (view, click, add to cart)
    • Custom pixel support with arbitrary parameters
    • Automatic retry logic and error handling
    • Debug mode with pixel validation
    • Integration with existing Discovery APIs
  • NEW: Recommendations & Pathways API - Complete widget-based recommendation system
    • Keyword-based recommendations with faceting
    • Item-based "More like this" recommendations
    • Category browsing with advanced filtering
    • Personalized user recommendations
    • Global trending/popular products
    • Recently viewed products tracking
  • Enhanced faceting support across all recommendation widgets
  • Context-based merchandising with context_pids parameter
  • Filter facets for maintaining UI state during filtering

0.4.0 #

  • Added Autosuggest API support
  • Enhanced product search with grouping
  • Improved facet handling with unified v3.0 format

0.3.0 #

  • Added Visual Search functionality
  • Improved error handling

0.2.0 #

  • Updated dependencies
  • Enhanced API client stability

0.1.0 #

  • Initial release with Product Search and Category API

Support #

License #

This project is licensed under the Apache 2.0 License - see the LICENSE file for details.


Note: This SDK requires a valid Bloomreach Discovery API Account. Contact Bloomreach for account setup and API access.

3
likes
140
points
53
downloads

Publisher

verified publisherpub.bloomreach.works

Weekly Downloads

Bloomreach Discovery - API SDK for executing autosuggest, product, category and visual search within a Flutter application

Homepage

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

collection, http, intl, meta

More

Packages that depend on brdiscovery