HaulPass

HaulPass 2.0 - Technical Architecture & Alpha Launch Guide

πŸ—οΈ System Architecture Overview

Architecture Principles

🎯 Cross-Platform Implementation Strategy

Platform Priority Matrix

Priority Level:        Web β”‚ PWA β”‚ iOS β”‚ Android
═══════════════════════════════════════════════
Development Speed      Highβ”‚Med  β”‚Low  β”‚Low
User Reach            Highβ”‚High β”‚Med  β”‚High  
Feature Completeness   Lowβ”‚High β”‚High β”‚High
Native Performance    Med β”‚High β”‚High β”‚High
Offline Capability    Med β”‚High β”‚High β”‚High
App Store Approval     N/A β”‚N/A  β”‚Low  β”‚Low

Development Approach

Phase 1: Core Platform (Weeks 1-2)

Single Codebase: Flutter Web
Primary Target: Web browsers
Features: Core functionality, authentication, basic tracking
Success Criteria: Functional MVP for alpha testing

Phase 2: Progressive Web App (Weeks 3-4)

Enhancement: Add PWA capabilities
Features: Offline support, push notifications, installable
Success Criteria: Mobile-optimized experience

Phase 3: Native Mobile (Weeks 5-8)

Deployment: iOS App Store, Google Play Store
Features: Full native integration, background tracking
Success Criteria: Production-ready mobile apps

πŸ“± Technical Implementation Details

Frontend Architecture

Flutter Web (Primary Platform)

// Project structure optimized for web deployment
lib/
β”œβ”€β”€ main.dart                    // Web-optimized entry point
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ config/
β”‚   β”‚   β”œβ”€β”€ app_config.dart     // Environment configuration
β”‚   β”‚   └── web_config.dart     // Web-specific settings
β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”œβ”€β”€ web_service.dart    // Web-specific APIs
β”‚   β”‚   └── offline_service.dart // Offline data management
β”‚   └── theme/
β”‚       └── web_theme.dart      // Web-optimized styling
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ models/                 // Data models with web serialization
β”‚   β”œβ”€β”€ repositories/           // Repository pattern for data access
β”‚   └── providers/              // Riverpod providers
β”œβ”€β”€ presentation/
β”‚   β”œβ”€β”€ screens/                // Web-optimized screens
β”‚   β”‚   β”œβ”€β”€ home/
β”‚   β”‚   β”œβ”€β”€ elevator_search/
β”‚   β”‚   └── tracking/
β”‚   β”œβ”€β”€ widgets/                // Responsive web widgets
β”‚   β”‚   β”œβ”€β”€ responsive_layout.dart
β”‚   β”‚   β”œβ”€β”€ web_navigation.dart
β”‚   β”‚   └── touch_optimized.dart
β”‚   └── providers/              // State management
└── utils/
    β”œβ”€β”€ web_utils.dart          // Web-specific utilities
    └── geo_utils.dart          // Geographic calculations

Key Web Optimizations

// Responsive design patterns
class ResponsiveLayout extends StatelessWidget {
  final Widget mobile;
  final Widget? tablet;
  final Widget desktop;

  const ResponsiveLayout({
    Key? key,
    required this.mobile,
    this.tablet,
    required this.desktop,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth >= 1200) {
          return desktop;
        } else if (constraints.maxWidth >= 768) {
          return tablet ?? mobile;
        } else {
          return mobile;
        }
      },
    );
  }
}

// Touch-optimized interactions
class TouchOptimizedButton extends StatelessWidget {
  final Widget child;
  final VoidCallback onTap;
  final double minTouchSize = 44.0; // Accessibility standard

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        constraints: BoxConstraints(
          minHeight: minTouchSize,
          minWidth: minTouchSize,
        ),
        padding: EdgeInsets.all(16),
        child: Center(child: child),
      ),
    );
  }
}

State Management with Riverpod 2.x

Provider Architecture

// Main app providers
@riverpod
class AppNotifier extends _$AppNotifier {
  @override
  AppState build() {
    _initializeApp();
    return AppState();
  }
  
  void _initializeApp() {
    // Initialize services, check connectivity, load user data
  }
}

// Feature-specific providers
@riverpod
class ElevatorNotifier extends _$ElevatorNotifier {
  @override
  ElevatorState build() {
    loadNearbyElevators();
    return ElevatorState();
  }
}

// Computed providers for derived state
@riverpod
bool isNearElevator(IsNearElevatorRef ref) {
  final location = ref.watch(currentLocationProvider);
  final elevators = ref.watch(elevatorsProvider);
  
  if (location == null || elevators.isEmpty) return false;
  
  return elevators.any((elevator) => 
    calculateDistance(location, elevator.location) < 0.5 // 500m
  );
}

Backend Integration (Supabase)

Database Schema

-- User management
CREATE TABLE profiles (
  id UUID REFERENCES auth.users PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  full_name TEXT,
  truck_number TEXT,
  company TEXT,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

-- Elevator data
CREATE TABLE elevators (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  company TEXT NOT NULL,
  location GEOGRAPHY(POINT, 4326) NOT NULL,
  address TEXT,
  accepted_grains TEXT[],
  capacity_bushels INTEGER,
  is_active BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

-- Real-time status
CREATE TABLE elevator_status (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  elevator_id UUID REFERENCES elevators(id),
  current_wait_time INTEGER, -- minutes
  trucks_in_line INTEGER DEFAULT 0,
  accepting_grain TEXT,
  status TEXT CHECK (status IN ('open', 'full', 'closed', 'maintenance')),
  last_updated TIMESTAMP DEFAULT NOW()
);

-- Tracking sessions
CREATE TABLE hauling_sessions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES profiles(id),
  elevator_id UUID REFERENCES elevators(id),
  start_time TIMESTAMP NOT NULL,
  end_time TIMESTAMP,
  grain_type TEXT,
  status TEXT CHECK (status IN ('active', 'completed', 'cancelled')),
  notes TEXT,
  created_at TIMESTAMP DEFAULT NOW()
);

-- Location tracking
CREATE TABLE location_tracks (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  session_id UUID REFERENCES hauling_sessions(id),
  user_id UUID REFERENCES profiles(id),
  location GEOGRAPHY(POINT, 4326) NOT NULL,
  accuracy FLOAT,
  speed FLOAT,
  recorded_at TIMESTAMP DEFAULT NOW()
);

Real-time Subscriptions

// Real-time elevator status updates
class ElevatorStatusService {
  late RealtimeChannel _statusChannel;
  
  void initialize() {
    _statusChannel = Supabase.instance.client
        .channel('elevator_status_changes')
        .onPostgresChanges(
          event: PostgresChangeEvent.update,
          schema: 'public',
          table: 'elevator_status',
          callback: _handleStatusUpdate,
        )
        .subscribe();
  }
  
  void _handleStatusUpdate(PostgresChangePayload payload) {
    final status = ElevatorStatus.fromJson(payload.newRecord);
    ref.read(elevatorStatusProvider(status.elevatorId).notifier).update(status);
  }
}

Location Services

GPS Tracking Implementation

class LocationTrackingService {
  StreamSubscription<Position>? _positionSubscription;
  final GeolocatorPlatform _geolocator = GeolocatorPlatform.instance;
  
  Future<void> startTracking() async {
    // Check permissions
    final permission = await _geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      await _geolocator.requestPermission();
    }
    
    // Start position stream
    const locationSettings = LocationSettings(
      accuracy: LocationAccuracy.high,
      distanceFilter: 10, // Update every 10 meters
    );
    
    _positionSubscription = _geolocator
        .getPositionStream(locationSettings: locationSettings)
        .listen(_handlePositionUpdate);
  }
  
  void _handlePositionUpdate(Position position) {
    final location = Location(
      latitude: position.latitude,
      longitude: position.longitude,
      altitude: position.altitude,
      accuracy: position.accuracy,
      timestamp: DateTime.now(),
    );
    
    // Update location provider
    ref.read(currentLocationProvider.notifier).updateLocation(location);
    
    // Auto-detect nearby elevators
    _checkProximityToElevators(location);
  }
}

πŸ”§ Development Workflow for Alpha Launch

Local Development Setup

# 1. Clone and setup
git clone <repository-url>
cd haulpass_new
flutter pub get

# 2. Generate code
dart run build_runner build --delete-conflicting-outputs

# 3. Configure environment
cp .env.example .env
# Edit .env with Supabase credentials

# 4. Run web version
flutter run -d chrome

# 5. Test PWA features
flutter run -d chrome --web-renderer html
# Check: Offline functionality, service worker, installability

# 6. Mobile testing
flutter run -d android  # or ios

Testing Strategy

Unit Tests (Minimum 80% Coverage)

// Example test structure
testWidgets('User can sign in with valid credentials', (tester) async {
  // Arrange
  await tester.pumpWidget(
    ProviderScope(
      child: MaterialApp(home: SignInScreen()),
    ),
  );
  
  // Act
  await tester.enterText(find.byKey(Key('email')), 'test@example.com');
  await tester.enterText(find.byKey(Key('password')), 'password123');
  await tester.tap(find.byKey(Key('sign_in_button')));
  
  // Assert
  await tester.pumpAndSettle();
  expect(find.byType(HomeScreen), findsOneWidget);
});

Integration Tests (Critical User Journeys)

// Test complete hauling workflow
testWidgets('Complete hauling workflow', (tester) async {
  // Setup app with test data
  await loadTestData();
  
  // 1. Sign in
  await signIn(tester, 'testuser@example.com', 'password');
  
  // 2. Search for elevators
  await searchElevators(tester, 'wheat');
  expect(find.byType(ElevatorList), findsOneWidget);
  
  // 3. Start tracking
  await startTracking(tester, firstElevator);
  
  // 4. Navigate to elevator
  await navigateToElevator(tester);
  
  // 5. Complete session
  await completeSession(tester);
  
  // 6. Verify session saved
  expect(find.byType(SessionSummary), findsOneWidget);
});

Performance Testing

// Web performance benchmarks
testWidgets('App loads within 3 seconds', (tester) async {
  final stopwatch = Stopwatch()..start();
  
  await tester.pumpWidget(MyApp());
  await tester.pumpAndSettle();
  
  stopwatch.stop();
  expect(stopwatch.elapsedMilliseconds, lessThan(3000));
});

// Memory usage monitoring
testWidgets('No memory leaks during GPS tracking', () async {
  final initialMemory = await getMemoryUsage();
  
  // Start GPS tracking
  await startLocationTracking();
  await waitFor(Duration(seconds: 60));
  
  final finalMemory = await getMemoryUsage();
  expect(finalMemory - initialMemory, lessThan(50 * 1024 * 1024)); // 50MB limit
});

Deployment Strategy

Web Deployment (Primary)

# GitHub Actions workflow
name: Deploy to Web
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.19.0'
      - run: flutter pub get
      - run: dart run build_runner build --delete-conflicting-outputs
      - run: flutter build web --release --web-renderer html
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: $
          publish_dir: ./build/web

PWA Configuration

// web/manifest.json
{
  "name": "HaulPass - Grain Hauling Solution",
  "short_name": "HaulPass",
  "description": "Professional grain hauling logistics and tracking",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2196F3",
  "icons": [
    {
      "src": "icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Mobile App Deployment

# iOS Deployment
name: Deploy iOS App
on:
  push:
    tags: ['v*']

jobs:
  deploy-ios:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      - uses: subosito/flutter-action@v2
      - run: flutter build ios --release
      - uses: apple-actions/download-certificates@v2
        with:
          issuer-id: $
          api-key-id: $
          api-private-key: $
      - uses: maierj/fastlane-action@v3.0.0
        with:
          lane: release
          options: '{"app_identifier":"com.haulpass.app"}'

πŸ“Š Monitoring & Analytics

Application Monitoring

// Error tracking and performance monitoring
class MonitoringService {
  static void initialize() {
    // Initialize crash reporting
    FlutterError.onError = (details) {
      FirebaseCrashlytics.instance.recordError(
        details.exception,
        details.stack,
        fatal: true,
      );
    };
    
    // Performance monitoring
    PerformanceObserver((list) {
      for (final entry in list) {
        FirebasePerformance.instance
            .newTrace(entry.name)
            .start();
      }
    });
  }
}

User Analytics

// Privacy-compliant analytics
class AnalyticsService {
  static void trackScreenView(String screenName) {
    // Only track if user opted into analytics
    if (_userConsent.analytics) {
      Amplitude.instance.logEvent('screen_view', eventProperties: {
        'screen_name': screenName,
        'timestamp': DateTime.now().toIso8601String(),
      });
    }
  }
  
  static void trackFeatureUsage(String feature) {
    if (_userConsent.analytics) {
      Amplitude.instance.logEvent('feature_used', eventProperties: {
        'feature': feature,
        'user_type': _userProfile.userType,
      });
    }
  }
}

🚨 Critical Issues & Solutions

Current Build Issues (Must Fix Before Alpha)

1. AuthState Naming Conflict

// Current Issue: AuthState conflicts with Supabase's AuthState
// Solution: Use explicit imports
import 'package:supabase_flutter/supabase_flutter.dart' as supabase;

// Update all references
void _handleAuthStateChange(supabase.AuthStateChange<supabase.Session> data) {
  // Implementation
}

2. Location Naming Conflicts

// Current Issue: Location class conflicts with geocoding Location
// Solution: Use typedefs and explicit imports
import 'package:geolocator/geolocator.dart';
import 'package:geocoding/geocoding.dart' as geo;
import 'location_models.dart'; // Our Location class

typedef GeoPlacemark = geo.Placemark;

3. Riverpod Provider Ref Types

// Current Issue: Deprecated ref types
// Solution: Update to new syntax
@riverpod
class AuthNotifier extends _$AuthNotifier {
  @override
  AuthState build() {
    // Remove deprecated ref type usage
    return const AuthState();
  }
}

// Update providers to use simple Ref
@riverpod
UserProfile? currentUser(Ref ref) {
  final authState = ref.watch(authNotifierProvider);
  return authState.user;
}

Performance Optimizations

GPS Battery Optimization

class OptimizedLocationTracking {
  static const _normalInterval = Duration(seconds: 30);
  static const _highAccuracyInterval = Duration(seconds: 5);
  
  Timer? _updateTimer;
  
  void startAdaptiveTracking() {
    _updateTimer = Timer.periodic(_normalInterval, (timer) {
      final speed = _currentSpeed;
      if (speed > 60) { // Moving fast
        _increaseAccuracy();
      } else {
        _decreaseAccuracy();
      }
    });
  }
}

Memory Management

class MemoryEfficientImageLoading {
  static Widget cachedImage(String url) {
    return CachedNetworkImage(
      imageUrl: url,
      memCacheWidth: 800, // Limit memory usage
      memCacheHeight: 600,
      placeholder: (context, url) => CircularProgressIndicator(),
      errorWidget: (context, url, error) => Icon(Icons.error),
    );
  }
}

🎯 Alpha Launch Checklist

Technical Readiness

User Experience

Business Readiness

Alpha Testing Preparation

πŸ”„ Continuous Improvement Process

Weekly Sprint Planning

  1. Monday: Review user feedback and analytics
  2. Tuesday: Plan and prioritize improvements
  3. Wednesday-Thursday: Development and testing
  4. Friday: Deploy improvements and monitor

Metrics to Monitor

Release Strategy


This technical guide should be updated regularly to reflect the evolving architecture and requirements as HaulPass moves from alpha to production.