My App

Plan

SmartCrew Mobile - Plan

Technical Specification: Automated Advanced Exception Handling System

Project Context

Application: SmartCrew Mobile (Flutter) Architecture: Provider + Firebase + Hive. Goal: Implement a unified, automated exception handling system that eliminates manual try-catch blocks in business logic, ensures UI feedback (Toasts) without BuildContext context crashes, and separates User Errors from System Crashes.

General Directives for AI Implementation

  1. Clean Code: Follow SOLID principles, specifically Open/Closed for exceptions and Single Responsibility for the executor/mapper.
  2. Safety: Ensure scaffoldMessengerKey checks for null before execution to prevent null pointer exceptions.
  3. No Context: Logic layers must not import package:flutter/material.dart just to get BuildContext. They should use the mixin and services defined below.
  4. Logging: Ensure only critical system errors go to the Crashlytics placeholder; validation errors remain in the console.

Phase 1: Core Infrastructure (Models & Entry Point)

Objective: Define the polymorphic exception model and setup the global UI key.

Task 1.1: Exception Hierarchy

Path: lib/core/error/ File: base_exception.dart Requirement: Create an abstract AppException.

  • Fields: message, title, originalError (dynamic), stackTrace.
  • Getters: bool logToCrashlytics, UiStyle uiStyle (enum: dialog, toast, silent).

File: ui_style.dart

  • Enum UiStyle { dialog, toast, silent }

File: exceptions/user_exceptions.dart

  • Extend AppException. logToCrashlytics always returns false.
  • Classes:
    • ValidationException (Style: Dialog/Toast, default title: "Action Required")
    • NetworkException (Style: Toast, message: "No Internet Connection")
    • DataMissingException (Style: Dialog, accepts missingItem and fixRoute)

File: exceptions/system_exceptions.dart

  • Extend AppException. logToCrashlytics always returns true.
  • Classes:
    • FatalErrorException (Style: Dialog, generic friendly message, logs original error).
    • SilentSyncException (Style: Silent, logs original error).

Task 1.2: Global Navigation Key

Path: lib/smart_crew_app.dart or lib/main.dart Requirement:

  1. Define a top-level global key:
    final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
  2. Assign this key to the scaffoldMessengerKey property of your MaterialApp.

Phase 2: Translation & UI Service

Objective: Bridge the gap between raw Dart/Firebase errors and the user UI without context passing.

Task 2.1: Central Error Mapper

Path: lib/core/error/error_mapper.dart Requirement:

  • Create class ErrorMapper.
  • Method: static AppException map(dynamic error, [StackTrace? stack]).
  • Logic:
    • If error is AppException, return it.
    • If error is SocketException -> return NetworkException.
    • If error is FirebaseException:
      • code permission-denied -> return ValidationException("Access Denied").
      • code unavailable -> return NetworkException.
    • Else -> return FatalErrorException (wrap original error).

Task 2.2: Context-Less Toast Service

Path: lib/core/services/toast_service.dart Requirement:

  • Import the rootScaffoldMessengerKey from Phase 1.
  • Method: static void show(String message, {ToastType type}).
  • Types: success (Green), warning (Orange), error (Red).
  • Implementation: Use rootScaffoldMessengerKey.currentState?.showSnackBar(...).
  • Style: SnackBar behavior must be floating.

Phase 3: The Automation Engine (Mixin)

Objective: The component that Providers will consume to get "free" error handling.

Task 3.1: Safe Execution Mixin

Path: lib/core/mixins/safe_execution_mixin.dart Requirement:

  • Define mixin SafeExecutionMixin.
  • Property: final ValueNotifier<bool> isBusyNotifier = ValueNotifier(false);
  • Method:
    Future<void> execute(
      Future<void> Function() action, {
      String? successMessage,
      // optional: custom onError callback for special cases
    }) async
  • Logic Flow:
    1. Set isBusyNotifier.value = true.
    2. try -> await action().
    3. If successMessage != null -> ToastService.show(success, type: success).
    4. catch(e, stack):
      • final exception = ErrorMapper.map(e, stack);
      • If exception.logToCrashlytics -> Print/Log to Crashlytics.
      • If exception.uiStyle != Silent:
        • Determine Toast Type (Warning for UserException, Error for SystemException).
        • ToastService.show(exception.message, type: ...)
    5. finally -> Set isBusyNotifier.value = false.

Phase 4: Integration Example

Objective: Demonstrate how to apply this to an existing provider (refactor).

Task 4.1: Refactor ManualLogsProvider

Path: lib/providers/manual_logs_provider.dart Action:

  1. Add with SafeExecutionMixin to the class definition.
  2. Locate submitLog (or similar method).
  3. Remove all existing try-catch, isLoading = true/false, and context-dependent Dialog calls.
  4. Wrap logic in await execute(() async { ... }).
  5. Add validation:
    if (project == null) throw const ValidationException('Select a project');

Success Criteria for Verification

Before accepting the code, verify the following:

  1. The Context Test: Can ToastService.show() be called from inside a Provider method without passing context arguments? (Yes/No).
  2. The Network Test: Turn off internet simulator. Call a provider function. Does a "Network Exception" toast appear automatically? (Yes/No).
  3. The User Validation: Submit a form with missing data (throwing ValidationException). Does the toast appear as Orange (Warning) and NOT Red (System Error)? (Yes/No).
  4. The Crash Log: Does throwing Exception("Random error") result in a FatalErrorException being mapped and logged to console/Crashlytics wrapper? (Yes/No).
  5. Clean Logic: Does ManualLogsProvider contain ZERO try-catch blocks inside the refactored method? (Yes/No).

On this page