Voyage πŸš—

A comprehensive Flutter plugin for the Kruzr 360 SDK, providing advanced driving analytics, trip monitoring, and user behavior analysis capabilities.

Version Flutter Dart

πŸ“‹ Table of Contents

🌟 Overview

Voyage is a Flutter plugin that integrates with the Kruzr 360 SDK to provide comprehensive driving analytics and trip monitoring capabilities. It enables developers to build applications with advanced telematics features including real-time trip tracking, driving behavior analysis, user ranking systems, and detailed performance metrics.

✨ Features

  • πŸš€ SDK Integration - Easy initialization and platform version detection
  • πŸ“± Trip Monitoring - Automatic trip detection and real-time monitoring
  • πŸ‘€ User Management - Registration, authentication, and profile management
  • πŸ“Š Analytics & Metrics - Comprehensive driving behavior analysis
  • πŸ† Leaderboards - User rankings and performance comparison
  • πŸŽ–οΈ Achievements - Track progress across driving behavior categories
  • 🎁 Rewards System - Earn and redeem rewards based on driving performance
  • 🌐 Data Synchronization - Automatic and manual data sync capabilities
  • 🎯 Trip Control - Manual trip start/stop functionality
  • πŸ—ΊοΈ Trip Route Visualization – Retrieve full route GeoJSON for rendering
  • πŸ“€ Trip Sharing - Generate shareable trip URLs
  • πŸ“Ά Bluetooth Support - Device scanning and management

πŸ“¦ Installation

Add this to your pubspec.yaml file:

dependencies:
  voyage: ^0.0.4-canary.57

Then run:

flutter pub get

πŸš€ Getting Started

1. Initializes the Kruzr 360 SDK with the provided configuration.

This method must be called before using any other functionality of the Kruzr 360 SDK. It establishes the connection with the underlying native SDK and configures various SDK behaviors and settings.

Parameters:

  • kruzr360InitConfig: Configuration object containing all required initialization settings:
    • licenseKey: Valid license key provided by Kruzr for authentication
    • appName: Name of your application for identification
    • notificationChannelId: Id for the notification channel (Android)
    • shouldTripAutoEnd: Whether trips should automatically end (default: true)
    • shouldTripAutoStart: Whether trips should automatically start (default: true)
    • allowEventSyncRealTime: Whether to sync events in real-time (default: true)
    • shouldTripAutoStartOnlyWhenVehicleConnected: Whether trips should automatically start only when a saved vehicle is connected (default: false)

Usage:

try {
  final config = Kruzr360InitConfig(
    licenseKey: 'your-license-key-here',
    appName: 'My Driving App',
    notificationChannelId: 'co.mycompany.myapp.notification.CHANNEL_ID_TELEMATICS',
    shouldTripAutoEnd: true,
    shouldTripAutoStart: true,
    allowEventSyncRealTime: false,
    shouldTripAutoStartOnlyWhenVehicleConnected: false,
  );
  await Kruzr360Communicator.initializeSDK(config);
  print('SDK initialized successfully');
} catch (e) {
  print('Failed to initialize SDK: $e');
}

Throws:

  • Future.error("Unable to initialize SDK"): When the SDK fails to initialize due to platform-specific issues, invalid license key, or other initialization errors.

Platform Exceptions Handled:

  • PlatformException: Catches platform-specific errors from native Android/iOS code
  • Exception: Catches any other general exceptions during initialization

Important Notes:

  • This is a static method and should be called before creating any instance of Kruzr360Communicator
  • The method will log detailed error information in debug mode for troubleshooting purposes
  • Initialization should typically be done in your main.dart file or during app startup
  • Configuration settings affect SDK behavior throughout the app lifecycle

Example Implementation:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  try {
    final config = Kruzr360InitConfig(
      licenseKey: 'your-license-key',
      appName: 'My App',
      notificationChannelId: 'co.mycompany.myapp.notification.CHANNEL_ID_TELEMATICS',
    );
    await Kruzr360Communicator.initializeSDK(config);
    runApp(MyApp());
  } catch (e) {
    // Handle initialization failure
    print('SDK initialization failed: $e');
    // Show error dialog or fallback UI
   }
}

2. Create a Communicator Instance

final communicator = Kruzr360Communicator(
  companyName: 'your-company-name',
  accountName: 'your-account-name',
);

3. Set Up Location Services

communicator.setupLocationStuffs();

βš™οΈ Configuration

Android Setup

Add the following permissions to your android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

πŸ“± Usage

User Registration and Authentication

// Register User
final userId = await communicator.registerUserToKruzr(
  driverId: 'unique_driver_123',
  name: 'John Doe',
  countryCode: '+1',
  phoneNumber: '1234567890',
  email: 'john.doe@example.com',
);

Trip Monitoring

// Initiate trip monitoring
await communicator.initiateTripMonitoring();

// Start a trip manually
final started = await communicator.startTrip();

// Get current trip data
final currentTrip = await communicator.getCurrentTripData();

// Stop a trip
final stopped = await communicator.stopTrip();

Enables or disables automatic trip start detection

Controls whether the SDK should automatically detect and start recording trips based on vehicle movement patterns. When enabled, the SDK will automatically begin trip recording when driving behavior is detected.

Parameters:

  • shouldAutoStart: true to enable automatic trip start, false to disable

Usage:

try {
  // Enable automatic trip start
  await communicator.setTripAutoStartEnabled(true);
  print('Automatic trip start enabled');

  // Disable automatic trip start (manual start only)
  await communicator.setTripAutoStartEnabled(false);
  print('Automatic trip start disabled - manual start required');
} catch (e) {
  print('Failed to change auto start setting: $e');
}

Throws:

  • Future.error("Unable to change trip auto start pref"): When setting update fails

Important Notes:

  • Setting persists across app sessions
  • When disabled, trips must be started manually using startTrip
  • Overrides the initial configuration from Kruzr360InitConfig
  • Changes take effect immediately for new trip detection

Use Cases:

  • User preference settings
  • Fleet management policies
  • Testing and debugging scenarios
  • Power management optimization

Retrieves the current automatic trip start setting.

Returns whether the SDK is currently configured to automatically detect and start recording trips based on vehicle movement patterns. Returns:

  • bool: true if automatic trip start is enabled, false otherwise Usage:
try {
  final autoStartEnabled = await communicator.getTripAutoStartEnabled();
  if (autoStartEnabled) {
    print('Automatic trip start is enabled');
    // Show UI indicating automatic mode
  } else {
    print('Manual trip start required');
    // Show manual start button
  }
} catch (e) {
  print('Failed to get auto start setting: $e');
}

Throws:

  • Future.error("Unable to get trip auto start pref"): When retrieval fails Use Cases:
  • Updating UI based on current settings
  • Validating configuration state
  • Settings screen initialization
  • Debugging trip detection issues

Retrieves the current automatic trip end setting.

Returns whether the SDK is currently configured to automatically detect and stop recording trips when the vehicle stops moving. Returns:

  • bool: true if automatic trip end is enabled, false otherwise Usage:
try {
  final autoEndEnabled = await communicator.getTripAutoEndEnabled();
  if (autoEndEnabled) {
    print('Automatic trip end is enabled');
    // Trips will end automatically when stopped
  } else {
    print('Manual trip end required');
    // Show manual stop button
  }
} catch (e) {
  print('Failed to get auto end setting: $e');
}

Throws:

  • Future.error("Unable to get trip auto end pref"): When retrieval fails Use Cases:
  • Updating UI based on current settings
  • Validating configuration state
  • Settings screen initialization
  • Debugging trip termination issues

Enables or disables automatic trip end detection.

Controls whether the SDK should automatically detect and stop recording trips when the vehicle stops moving for a sufficient duration. When enabled, the SDK will automatically end trip recording based on movement patterns. Parameters:

  • shouldAutoEnd: true to enable automatic trip end, false to disable Usage:
try {
  // Enable automatic trip end
  await communicator.setTripAutoEndEnabled(true);
  print('Automatic trip end enabled');
  // Disable automatic trip end (manual end only)
  await communicator.setTripAutoEndEnabled(false);
  print('Automatic trip end disabled - manual end required');
} catch (e) {
  print('Failed to change auto end setting: $e');
}

Throws:

  • Future.error("Unable to change trip auto end pref"): When setting update fails Important Notes:
  • Setting persists across app sessions
  • When disabled, trips must be stopped manually using stopTrip
  • Overrides the initial configuration from Kruzr360InitConfig
  • Changes take effect immediately for active trips Use Cases:
  • User preference settings
  • Fleet management policies
  • Long stop scenarios (gas stations, shopping)
  • Testing and debugging scenarios

Fetch Trip List

Retrieves a paginated list of trips recorded by the user.

Parameters:

  • offset: Starting index for pagination
  • length: Number of trips to retrieve

Returns:

  • List

Usage:

try {
  final trips = await communicator.fetchTripList(0, 20);
  print('Fetched ${trips.length} trips');
} catch (e) {
  print('Failed to fetch trip list: $e');
}

Throws:

  • Future.error("Unable to fetch trip list") Important Notes:
  • Supports pagination
  • Returns trips in descending order (latest first)
  • Requires user to be logged in Use Cases:
  • Trip history screen
  • Infinite scroll list
  • Trip archive browsing

Enables or disables real-time event synchronization.

Controls whether driving events and trip data should be synchronized with Kruzr servers immediately as they occur, or batched for later synchronization. Real-time sync provides immediate data availability but may impact battery life and data usage.

Parameters:

  • allowEventSyncRealTime: true to enable real-time sync, false for batch sync

Usage:

try {
  // Enable real-time synchronization
  await communicator.shouldEventSyncRealTime(true);
  print('Real-time sync enabled - events sync immediately');
  // Disable real-time sync (batch mode)
  await communicator.shouldEventSyncRealTime(false);
  print('Batch sync enabled - events sync periodically');
} catch (e) {
  print('Failed to change sync setting: $e');
}

Throws:

  • Future.error("Unable to change event sync real time pref"): When setting update fails Important Notes:
  • Setting persists across app sessions
  • Real-time sync requires active internet connection
  • Overrides the initial configuration from Kruzr360InitConfig
  • Affects battery life and data usage patterns
  • May be subject to network conditions and server availability

Performance Considerations:

  • Real-time enabled: Immediate data availability, higher battery/data usage
  • Real-time disabled: Better battery life, periodic data updates

Use Cases:

  • Fleet monitoring requiring immediate updates
  • Battery optimization for extended trips
  • Network-constrained environments
  • User preference settings

Retrieves the current real-time event synchronization setting.

Returns whether the SDK is currently configured to synchronize driving events and trip data immediately as they occur, or to batch them for later synchronization.

Returns:

  • bool: true if real-time sync is enabled, false for batch sync

Usage:

try {
  final realTimeSyncEnabled = await communicator.isEventSyncRealTimeEnabled();
  if (realTimeSyncEnabled) {
    print('Real-time sync active - immediate data updates');
    // Show real-time indicator in UI
  } else {
    print('Batch sync active - periodic data updates');
    // Show batch sync indicator
  }
} catch (e) {
  print('Failed to get sync setting: $e');
}

Throws:

  • Future.error("Unable to get event sync real time pref"): When retrieval fails

Use Cases:

  • Updating UI sync status indicators
  • Validating configuration state
  • Settings screen initialization
  • Performance monitoring and optimization
  • Debugging data synchronization issues

Enables or disables automatic trip start only when vehicle connected.

Controls whether the SDK should automatically detect and start recording trips only when a saved Bluetooth vehicle is connected. When enabled, automatic trip start will only occur if a previously saved vehicle is detected via Bluetooth. This provides more precise trip detection by ensuring trips only start when actually in a vehicle.

Parameters:

  • shouldAutoStartOnlyIfVehicleConnected: true to enable vehicle-dependent automatic start, false to allow any movement-based start

Usage:

try {
  // Enable automatic start only when vehicle connected
  await communicator.setAutoTripStartOnlyIfVehicleConnected(true);
  print('Automatic trip start enabled only for connected vehicles');

  // Disable vehicle-only restriction (normal auto start)
  await communicator.setAutoTripStartOnlyIfVehicleConnected(false);
  print('Automatic trip start works with any movement detection');
} catch (e) {
  print('Failed to change vehicle-connected auto start setting: $e');
}

Throws:

  • Future.error("Unable to change auto trip start only if vehicle connected pref"): When setting update fails

Important Notes:

  • Setting persists across app sessions
  • Requires at least one vehicle to be saved via saveVehicle
  • Works in conjunction with general automatic trip start setting
  • Overrides the initial configuration from Kruzr360InitConfig
  • Changes take effect immediately for new trip detection

Use Cases:

  • Preventing false trip starts from walking/cycling
  • Fleet management with assigned vehicles
  • Improving trip accuracy for multi-modal transport users
  • Battery optimization by reducing unnecessary trip monitoring

Retrieves the current automatic trip start only when vehicle connected setting.

Returns whether the SDK is currently configured to automatically detect and start recording trips only when a saved vehicle is connected via Bluetooth.

Returns:

  • bool: true if vehicle-connected auto start is enabled, false otherwise

Usage:

try {
  final vehicleOnlyAutoStart = await communicator.getAutoTripStartOnlyIfVehicleConnected();
  if (vehicleOnlyAutoStart) {
    print('Automatic trip start requires vehicle connection');
    // Show UI indicating vehicle dependency
  } else {
    print('Automatic trip start works with any movement');
    // Show normal auto start mode
  }
} catch (e) {
  print('Failed to get vehicle-connected auto start setting: $e');
}

Throws:

  • Future.error("Unable to get auto trip start only if vehicle connected pref"): When retrieval fails

Use Cases:

  • Updating UI based on current settings
  • Validating trip detection configuration
  • Settings screen initialization
  • Debugging trip detection behavior
  • Showing appropriate user guidance

Retrieve User Driving Streak

Returns information about the user’s current and longest continuous safe-driving streak.

A streak represents consecutive days with at least one valid trip that meets safety criteria defined by the Kruzr scoring engine.

Return

  • UserStreak
    • current: Current ongoing streak (days)
    • longest: Longest streak achieved (days) Usage:
try {
final streak = await communicator.getUserStreak();
print('Current streak: ${streak.current}');
print('Longest streak: ${streak.longest}');
} catch (e) {
print('Failed to get user streak: $e');
}

Throws:

  • Future.error("Unable to get user streak")

Important Notes:

  • Streaks are calculated server-side
  • A day counts only if at least one valid trip is completed
  • Data may be delayed if trips are not synced

Prerequisites:

  • User must be logged in
  • At least one completed trip synced to server

Use Cases:

  • Gamification and streak UI
  • Daily engagement nudges
  • Safe-driving habit tracking

πŸ“Š Retrieve Aggregated Trips Count

Returns the cumulative driving duration across all recorded trips. Returns:

  • int: Total driving duration in seconds

Usage:

try {
  final duration = await communicator.getAggregatedDrivingDuration();
  print('Total driving duration (seconds): $duration');
} catch (e) {
  print('Failed to get driving duration: $e');
}

Throws:

  • Future.error("Unable to get aggregated driving duration")

Important Notes:

  • Duration is calculated using trip start/end timestamps
  • Idle time outside active trips is excluded Use Cases:
  • Usage analytics
  • Insurance risk analysis
  • Fleet utilization insights

Get Aggregated Drive Time

Returns the total aggregated driving time across all recorded trips. Returns:

  • int: Total drive time in seconds

Usage:

try {
  final driveTime = await communicator.getAggregatedDriveTimeInSeconds();
  print('Total drive time (seconds): $driveTime');
} catch (e) {
  print('Failed to get aggregated drive time: $e');
}

Throws:

  • Future.error("Unable to get aggregated drive time")

Important Notes:

  • Calculated using trip start and end timestamps
  • Includes all completed trips
  • Excludes idle time outside active trips

Use Cases:

  • Driver activity tracking
  • Usage analytics
  • Fleet utilization insights

Get Aggregated Driving Behaviour Score

Returns the overall aggregated driving behaviour score calculated across all recorded trips. This score represents combined performance across driving categories such as speeding, braking, acceleration, and cornering.

Returns:

  • double: Aggregated driving behaviour score

Usage:

try {
  final score = await communicator.getAggregatedDrivingBehaviourScore();
  print('Aggregated behaviour score: $score');
} catch (e) {
  print('Failed to get aggregated behaviour score: $e');
}

Throws:

  • Future.error("Unable to get aggregated driving behaviour score")

Important Notes:

  • Calculated server-side
  • Based on all completed and synced trips
  • Reflects overall driving quality

Use Cases:

  • Dashboard score display
  • Driver performance insights
  • Gamification systems
  • Insurance scoring models

Get Aggregated Driving Behaviour Score Change

Returns the change in driving behaviour score compared to the previous evaluation period. A positive value indicates improvement, while a negative value indicates a decline in driving behaviour performance.

Returns:

  • double: Behaviour score change value

Usage:

try {
  final change = await communicator.getAggregatedDrivingBehaviourScoreChange();
  print('Behaviour score change: $change');
} catch (e) {
  print('Failed to get behaviour score change: $e');
}

Throws:

  • Future.error("Unable to get aggregated driving behaviour score change")

Important Notes:

  • Calculated server-side
  • Comparison period is determined by backend logic
  • Requires synced trip data for accurate results

Use Cases:

  • Performance trend indicators
  • Improvement tracking
  • Coaching feedback systems
  • Leaderboard delta comparison

πŸ”„ Data Synchronization

Manually Sync Trip Data

Forces synchronization of locally stored trip data with Kruzr servers. Useful when trips were recorded offline or during poor network conditions.

Returns:

  • Future<void>

Usage:

try {
  await communicator.syncTripData();
  print('Trip data sync triggered');
} catch (e) {
  print('Failed to sync trip data: $e');
}

Throws:

Future.error("Unable to sync trip data")

Important Notes:

  • Sync runs in background
  • Safe to call multiple times
  • Does not block UI thread

Use Cases:

  • Manual β€œSync Now” button
  • Offline-first applications
  • Debugging upload issues

Retrieve Pending Upload Count

Returns the number of trip files currently waiting to be uploaded to the server.

Returns:

  • int: Number of pending uploads

Usage:

try {
final pendingCount = await communicator.getPendingFilesCount();
print('Pending uploads: $pendingCount');
} catch (e) {
print('Failed to get pending files count: $e');
}
print('Failed to sync trip data: $e');
}

Throws:

  • Future.error("Unable to get pending files count")

Important Notes:

  • Count decreases automatically as sync completes
  • High count may indicate connectivity issues Use Cases:
  • Upload progress indicators
  • Network health diagnostics
  • Warning users before logout

Retrieve Pending Upload Size

Returns the total size in bytes of trip files currently waiting to be uploaded to the server.

Returns:

  • int: Total pending file size in bytes

Usage:

try {
  final size = await communicator.getPendingFilesSize();
  print('Pending upload size (bytes): $size');
} catch (e) {
  print('Failed to get pending files size: $e');
}

Throws:

  • Future.error("Unable to get pending files size")

Important Notes:

  • Size decreases automatically as sync completes
  • Includes unsynced trips and related event data

Use Cases:

  • Display upload size in MB
  • Data usage monitoring
  • Connectivity diagnostics

Enable WiFi-Only Data Sync

Enables or disables synchronization over WiFi only. When enabled, trip files will not upload using mobile data.

Parameters:

  • wifiOnly: true to allow WiFi-only sync, false to allow all networks

Returns:

  • Future

Usage:

try {
  await communicator.enableWifiOnlyDataSyncPref(true);
  print('WiFi-only sync enabled');
} catch (e) {
  print('Failed to update WiFi-only sync preference: $e');
}

Throws:

  • Future.error("Unable to update WiFi only sync preference")

Important Notes:

  • Setting persists across app sessions
  • Upload resumes automatically when WiFi becomes available

Use Cases:

  • Reduce mobile data usage
  • User-controlled sync behavior
  • Enterprise data policy enforcement

Check WiFi-Only Data Sync Status

Returns whether WiFi-only synchronization is currently enabled.

Returns:

  • bool: true if WiFi-only sync is enabled, false otherwise

Usage:

try {
  final wifiOnly = await communicator.isCurrentDataSyncPrefWifiOnly();
  print('WiFi-only sync enabled: $wifiOnly');
} catch (e) {
  print('Failed to get WiFi-only sync status: $e');
}

Throws:

  • Future.error("Unable to get WiFi only sync preference")

Use Cases:

  • Toggle UI state
  • Settings screen initialization
  • Sync configuration validation

Refresh File Sync Status

Forces a refresh of the internal file synchronization state and re-evaluates pending uploads.

Returns:

  • Future

Usage:

try {
  await communicator.refreshFileSyncStatus();
  print('File sync status refreshed');
} catch (e) {
  print('Failed to refresh file sync status: $e');
}

Throws:

  • Future.error("Unable to refresh file sync status")

Important Notes:

  • Re-checks pending file queue
  • Useful after network changes
  • Does not block UI

Use Cases:

  • Manual refresh button
  • Debugging sync issues
  • Revalidating upload state

Analytics and Metrics

// Get driving summary
final summary = await communicator.getDrivingSummary(
  DateTime.now().subtract(Duration(days: 30)),
  DateTime.now(),
  KruzrPerioicType.weekly,
);

// Get user ranking
final userRank = await communicator.getCurrentUserRank();

// Get leaderboard
final topDrivers = await communicator.getLeaderboardTop10();

// Get user achievements
final achievements = await communicator.fetchMyAchievements();
if (achievements != null) {
  // Check overall achievement progress
  print('Current tier: ${achievements.overall?.currentTier?.value}');
  print('Progress: ${achievements.overall?.count}');
  
  // Check specific category achievements
  if (achievements.speeding != null) {
    print('Speeding tier: ${achievements.speeding!.currentTier?.value}');
    print('Trips to next tier: ${achievements.speeding!.remainingTrips}');
  }
}

// Get available rewards
final availableRewards = await fetchAvailableRewards();
for (final reward in availableRewards) {
  print('Reward: ${reward.title}');
  print('Cost: ${reward.points} points');
  print('Description: ${reward.description}');
}

// Get earned rewards history
final earnedRewards = await fetchEarnedRewards();
if (earnedRewards.isNotEmpty) {
  print('You have earned ${earnedRewards.length} rewards!');
  for (final reward in earnedRewards) {
    print('Earned: ${reward.title} on ${reward.earnedOn}');
  }
}

// Get user profile tier requirements
final tierRequirements = await communicator.fetchUserProfileTierRequirements();
for (final tier in tierRequirements) {
  print('Tier: ${tier.name}');
  print('Required trips: ${tier.requiredTrips}');
  print('Required score: ${tier.requiredScore}');
  print('Description: ${tier.description}');
}

// Get user details including profile tier
final userDetails = await communicator.userDetails();
if (userDetails != null) {
  print('User name: ${userDetails.name}');
  print('Current profile tier: ${userDetails.profileTier.value}');
  print('Average trip score: ${userDetails.averageTripScore}');
  print('Total trips: ${userDetails.totalTripCount}');
  print('Total distance: ${userDetails.totalDistanceTravelled} km');
}

Retrieves possible intervention events that occurred during a specific trip.

Analyzes trip data to identify segments where driving interventions could have been beneficial, such as areas with poor driving behavior, potential safety issues, or coaching opportunities. Each intervention includes location data, timing, and associated scoring statistics.

Parameters:

  • appTripId: Unique identifier for the trip to analyze

Returns:

  • List<PossibleIntervention>: List of intervention events with detailed information

Usage:

try {
  final interventions = await communicator.getPossibleInterventionsForAppTripId('trip-123');
  for (final intervention in interventions) {
    print('Intervention type: ${intervention.scoringType}');
    print('Start time: ${intervention.startTime}');
    print('Location: ${intervention.startAddress}');

    // Check scoring statistics
    if (intervention.scoreStats != null) {
      intervention.scoreStats!.forEach((scoreType, stats) {
        if (stats != null) {
          print('$scoreType - Average: ${stats.average}, Max: ${stats.max}');
        }
      });
    }
  }
} catch (e) {
  print('Failed to get interventions: $e');
}

Throws:

  • Future.error("Unable to get possible interventions"): When retrieval fails

Intervention Data Includes:

  • Timing: Start and end timestamps in ISO 8601 format
  • Location: GPS coordinates and resolved addresses for start/end points
  • Scoring: Statistical data for various driving behavior categories
  • Type: Classification of the intervention (e.g., "SPEEDING", "HARD_BRAKING", etc.)

Common Intervention Triggers:

  • Hard braking events
  • Rapid acceleration
  • Sharp cornering
  • Speeding incidents
  • Distracted driving patterns
  • Drowsy driving detection

Use Cases:

  • Driver coaching and feedback
  • Safety analysis and reporting
  • Insurance risk assessment
  • Fleet management insights
  • Training program development

Prerequisites:

  • Trip must be completed and processed
  • Trip data must be synchronized with servers
  • Valid app trip ID is required

πŸ—ΊοΈ Fetching Trip Route (GeoJSON)

Fetch the complete trip route including start/end points, path, and event highlights.

try {
  final route = await communicator.fetchRoute("2985_1764285633333");

  if (route != null) {
    for (final feature in route.collection!) {
      print("Type: ${feature.type}");
      print("Properties: ${feature.properties}");
      print("Geometry: ${feature.geometry?.type}");
    }
  }
} catch (e) {
  print("Failed to fetch route: $e");
}

Route Contains:

  1. Route LineString (full trip path)

  2. Start and end points

  3. Speeding / braking / acceleration event markers

  4. Highlight segments with styling metadata

  5. Event metadata (startTime, scoreStats, etc.)

Works with any map render engine: MapLibre, Mapbox, flutter_map, Leaflet, or custom painter.

πŸš— Vehicle Management

Manage Bluetooth vehicle associations for automatic trip detection.

// Scan for nearby Bluetooth devices
final nearbyDevices = await communicator.scanForNearbyDevices();
for (final device in nearbyDevices) {
  print('Device: ${device.name} (${device.id})');
}

// Stop scanning when done
await communicator.stopScanningForNearbyDevices();
print('Stopped scanning');

// Get all paired devices (system-level paired devices)
final pairedDevices = await communicator.getAllPairedDevices();
for (final device in pairedDevices) {
  print('Paired: ${device.name} (${device.id})');
}

// Get currently connected devices
final connectedDevices = await communicator.getConnectedDevices();
for (final device in connectedDevices) {
  print('Connected: ${device.name} (${device.id})');
}

// Save a vehicle for automatic trip detection
final selectedVehicle = nearbyDevices.first; // User selects their car
communicator.saveVehicle(selectedVehicle);
print('Vehicle saved: ${selectedVehicle.name}');

// Get all saved vehicles
final savedVehicles = await communicator.getAllSavedVehicles();
for (final vehicle in savedVehicles) {
  print('Saved: ${vehicle.name} (${vehicle.id})');
}

// Remove a specific vehicle
final vehicleToRemove = savedVehicles.first;
communicator.removeSavedVehicle(vehicleToRemove);
print('Removed: ${vehicleToRemove.name}');

// Delete all saved vehicles (factory reset)
communicator.removeAllSavedVehicles();
print('All vehicles deleted');

Vehicle Management Features:

  • Automatic Detection: Saved vehicles enable automatic trip start/end
  • Multi-Vehicle Support: Save multiple vehicles for households/fleets
  • Granular Control: Remove specific vehicles or reset all
  • Persistent Storage: Vehicle associations survive app restarts

πŸ”— Trip Sharing

Generate Shareable Trip URL

Generates a publicly accessible URL for a completed trip. The URL can be viewed without authentication.

Parameters:

  • appTripId: Unique identifier of the completed trip

Returns:

  • String: Shareable trip URL

Usage:

try {
  final shareUrl =
      await communicator.generateShareableURL('trip_2985_1764285633333');
  print('Shareable URL: $shareUrl');
} catch (e) {
  print('Failed to generate shareable URL: $e');
}

Throws:

  • Future.error("Unable to generate shareable URL")

Important Notes:

  • Trip must be completed and synced
  • URL is publicly accessible

Prerequisites:

  • Valid appTripId
  • Trip data available on server

Use Cases:

  • Social sharing
  • Driver coaching reviews
  • Customer support investigations

Share Trip Directly

Shares a completed trip directly to a phone number.

Parameters:

  • appTripId: Unique identifier of the completed trip
  • countryCode: Country dialing code (for example, +91, +1)
  • phoneNumber: Recipient phone number

Returns:

  • Future

Usage:

try {
  await communicator.shareTrip(
    'trip_2985_1764285633333',
    '+91',
    '9876543210',
  );
  print('Trip shared successfully');
} catch (e) {
  print('Failed to share trip: $e');
}

Throws:

  • Future.error("Unable to share trip")

Important Notes:

  • Trip must be completed and synced
  • Requires active internet connection

Use Cases:

  • Fleet reporting
  • Driver coaching reviews
  • Sharing trip insights with others

πŸ‘€ User Account Management

Delete User Account

Deletes the currently logged-in user from Kruzr systems and clears all local SDK state.

Returns:

  • Future<void>

Usage:

try {
  await communicator.deleteUser();
  print('User account deleted successfully');
} catch (e) {
  print('Failed to delete user: $e');
}

Throws:

  • Future.error("Unable to delete user")

Important Notes:

  • Action is irreversible
  • All trips and analytics become inaccessible
  • SDK must be re-initialized for new user

Prerequisites:

  • User must be logged in

Use Cases:

  • Account deletion flows
  • Data privacy compliance (GDPR)
  • User off-boarding

Update User Profile

Updates editable user profile information on the server.

Parameters:

  • name (optional): Updated display name
  • email (optional): Updated email address

Returns:

  • Future<void>

Usage:

try {
  await communicator.updateUserProfile(
    name: 'John Doe',
    email: 'john.doe@example.com',
  );
  print('User profile updated');
} catch (e) {
  print('Failed to update user profile: $e');
}

Throws:

  • Future.error("Unable to update user profile")

Use Cases:

  • Profile edit screen
  • Account settings
  • Data correction

πŸ“– API Reference

Core SDK Methods

Method Description
initializeSDK(String licenseKey) Initialize the SDK with license key
getPlatformVersion() Get platform version information
setupLocationStuffs() Set up location services

Trip Monitoring

Method Description
initiateTripMonitoring() Enable automatic trip detection
startTrip() Manually start a trip
stopTrip() Manually stop a trip
getCurrentTripData() Get real-time trip information
fetchTripList(offset, length) Get paginated trip history
shareTrip(appTripId, countryCode, phoneNumber) Share trip via phone number

User Management

Method Description
registerUserToKruzr() Register a new user
logout() Log out current user
isLoggedIn() Check login status
userDetails() Get user information

Analytics & Metrics

Method Description
getUserStreak() Get driving streak information
getCurrentUserRank() Get user ranking
leaderboard() Get paginated leaderboard
getAggregatedDistanceTravelled() Get distance metrics
getDrivingSummary() Get comprehensive driving overview
getAggregatedDrivingScore() Get driving performance scores
getAggregatedDriveTimeInSeconds() Get aggregated drive time in seconds
getAggregatedDrivingBehaviourScore() Get category-level behaviour scores
getAggregatedDrivingBehaviourScoreChange() Get behaviour score change vs previous period
fetchMyAchievements() Get user achievements across all categories
fetchTripDetailsByAppTripId() Get historical trip details
fetchTripStatsByAppTripId() Get trip statistical analysis
getPossibleInterventionsForAppTripId() Get possible intervention events for a trip
fetchRoute() Fetch Trip Route (GeoJSON)
fetchAvailableRewards() Get all available rewards in the catalog
fetchEarnedRewards() Get user's earned rewards history
fetchUserProfileTierRequirements() Get user profile tier requirements metadata

Permission Management

Method Description
checkPermission(KruzrPermission) Check if a specific permission is granted
requestPermission(KruzrPermission) Request a permission from the user
openSettings() Open device settings for manual permission management
ensurePermissionOrOpenSettings(KruzrPermission) Complete permission flow with automatic escalation

Permission Management Details

checkPermission(KruzrPermission permission)

Checks if a specific permission has been granted without requesting it from the user.

// Check if location permission is granted
bool hasLocationPermission = await Kruzr360Communicator.checkPermission(
  KruzrPermission.location
);

if (hasLocationPermission) {
  // Permission is granted, proceed with location features
  print('Location permission granted');
} else {
  // Permission not granted, may need to request it
  print('Location permission not granted');
}

requestPermission(KruzrPermission permission)

Requests a specific permission from the user by showing the system permission dialog.

// Request camera permission
bool granted = await Kruzr360Communicator.requestPermission(
  KruzrPermission.camera
);

if (granted) {
  // Permission granted, can use camera features
  startCameraFeature();
} else {
  // Permission denied, show explanation or alternative
  showPermissionDeniedMessage();
}

openSettings()

Opens the device's application settings page where users can manually grant or revoke permissions.

// Open settings after explaining why permission is needed
await showDialog(
  context: context,
  builder: (context) => AlertDialog(
    title: Text('Permission Required'),
    content: Text('Please enable location permission in settings to use this feature.'),
    actions: [
      TextButton(
        onPressed: () async {
          Navigator.pop(context);
          await Kruzr360Communicator.openSettings();
        },
        child: Text('Open Settings'),
      ),
    ],
  ),
);

ensurePermissionOrOpenSettings(KruzrPermission permission)

Implements a comprehensive permission handling strategy that automatically escalates through available options.

// Complete permission flow for location
bool hasPermission = await Kruzr360Communicator.ensurePermissionOrOpenSettings(
  KruzrPermission.location
);

if (hasPermission) {
  // Permission granted, start location-based feature
  startLocationTracking();
} else {
  // Settings were opened, show message about checking permission again
  showMessage('Please grant location permission in settings and restart the feature.');
}

Available KruzrPermission Types:

  • KruzrPermission.location - Location access permissions
  • KruzrPermission.camera - Camera access permissions
  • KruzrPermission.microphone - Microphone access permissions
  • KruzrPermission.bluetooth - Bluetooth access permissions
  • KruzrPermission.storage - Storage access permissions

Data Management

Method Description
syncTripData() Manually sync trip data
refreshFileSyncStatus() Refresh file sync state from server
getPendingFilesCount() Get count of pending uploads
getPendingFilesSize() Get total size of pending uploads (bytes)
enableWifiOnlyDataSyncPref(bool) Enable/disable WiFi-only sync
isCurrentDataSyncPrefWifiOnly() Check if WiFi-only sync is enabled

Sharing & Bluetooth

Method Description
generateShareableURL() Create shareable trip URLs
shareTrip() Share trip directly via phone number
bluetoothScanEventStream() Get Bluetooth event stream
scanForNearbyDevices() Scan for nearby Bluetooth devices
stopScanningForNearbyDevices() Stop active Bluetooth scan
getAllPairedDevices() Retrieve all paired Bluetooth devices
getConnectedDevices() Retrieve currently connected devices
saveVehicle() Save a Bluetooth device as known vehicle
getAllSavedVehicles() Get all saved vehicle devices
editSavedVehicle() Edit specific saved vehicle
removeSavedVehicle() Remove specific saved vehicle
removeAllSavedVehicles() Remove all saved vehicles

πŸ” Permissions

This plugin requires the following permissions:

Android

  • ACCESS_FINE_LOCATION - For accurate location tracking
  • ACCESS_COARSE_LOCATION - For general location services
  • ACCESS_BACKGROUND_LOCATION - For background trip monitoring
  • BLUETOOTH & BLUETOOTH_ADMIN - For Bluetooth device management

iOS

  • Location permissions will be requested automatically
  • Bluetooth permissions for device scanning
  • If you're testing in simulators, use following commands before running the application:
  • pod deintegrate && rm -rf podfile.lock && rm -rf ~/Library/Developer/Xcode/DerivedData && pod install --repo-update

πŸ’‘ Examples

See the example/ directory for a complete Flutter application demonstrating the usage of the Voyage plugin.

πŸ› Issues and Support

If you encounter any issues or have questions:

  1. Check the documentation
  2. Search existing issues
  3. Create a new issue with detailed information

πŸ“„ License

This project is licensed under the terms specified in the LICENSE file.

🀝 Contributing

Contributions are welcome! Please read the contributing guidelines before submitting pull requests.


Made with ❀️ by the Kruzr Team