0

I'm using the workmanager Flutter plugin (workmanager: ^0.8.0) to perform background tasks, and I'm encountering the following crash when the background task is triggered:

NullPointerException in `DartExecutor$DartCallback.toString()

App is in production mode and user face this issue after app update, on reinstalling there is no crash.

Error log:

Exception java.lang.NullPointerException: 
Attempt to read from field 'java.lang.String io.flutter.view.FlutterCallbackInformation.callbackLibraryPath' on a null object reference 
in method 'java.lang.String io.flutter.embedding.engine.dart.DartExecutor$DartCallback.toString()' 
at io.flutter.embedding.engine.dart.DartExecutor$DartCallback.toString (DartExecutor.java:426) 
at java.lang.String.valueOf (String.java:4112) 
at java.lang.StringBuilder.append (StringBuilder.java:179) 
at io.flutter.embedding.engine.dart.DartExecutor.executeDartCallback (DartExecutor.java:181)

What I've done so far:

@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    // Add null safety checks
    if (task.isEmpty) {
      'Background task is null or empty'.logs();
      return Future.value(false);
    }

    try {
      // Ensure Flutter binding is initialized
      try {
        WidgetsFlutterBinding.ensureInitialized();
      } catch (e) {
        'Flutter binding failed: $e'.logs();
        return Future.value(false);
      }

      // Add a small delay to ensure proper initialization
      await Future.delayed(Duration(milliseconds: 100));

      // Initialize dependencies with null checks
      try {
        await AppStorage.init();
        final appStorage = AppStorage();
        Get.put(appStorage, permanent: true);
      } catch (storageError) {
        'AppStorage initialization failed: $storageError'.logs();
        return Future.value(false);
      }

      'Executing background sync task: $task'.logs();

      // Check sync status with null safety
      try {
        final isSyncInProgress = await GlobalSyncStatus.isAnySyncInProgress();
        if (isSyncInProgress == true) {
          'Background sync skipped: some sync operations already in progress'
              .logs();
          return Future.value(true);
        }
      } catch (syncCheckError) {
        'Sync status check failed: $syncCheckError'.logs();
        // Continue with sync attempt
      }

      try {
        // Perform sync operations with individual error handling
        await _performSyncWithErrorHandling();

        'Background sync task completed successfully.'.logs();
        return Future.value(true);
      } catch (e) {
        'Error during background sync: $e'.logs();
        return Future.value(false);
      }
    } catch (e, stackTrace) {
      'Background sync task failed: $e\n$stackTrace'.logs();
      try {
        await GlobalSyncStatus.releaseAllSyncLocks();
      } catch (_) {
        // Ignore cleanup errors
      }
      return Future.value(false);
    }
  });
}

// Helper method for safer sync operations
Future<void> _performSyncWithErrorHandling() async {
  final syncOperations = [
    () => LocalDataSyncService.performAttendanceSync(),
    () => LeaveLocalDataSyncService.performLeaveSync(),
    () => CashAdvLocalDataSyncServices.performCashAdvSync(),
    () => SalaryAdvLocalDataSyncServices.performSalaryAdvSYnc(),
    () => VisitLocalDataSyncServices.performClientVisitSync(),
  ];

  for (final operation in syncOperations) {
    try {
      await operation();
    } catch (e) {
      'Individual sync operation failed: $e'.logs();
    }
  }
}

void main() async {
  try {
    await AppStorage.init();
    Get.put(AppStorage(), permanent: true);

    // ✅ FIX: Initialize WorkManager with proper error handling
    await _initializeWorkManager();

    runApp(DevicePreview(
      enabled: false,
      builder: (context) => MyApp(
        flavorConfig: FlavorConfig.development(),
      ),
    ));
    VersionHelper.getAppVersionAndSave();
  } catch (e, stackTrace) {
    'Error during app initialization: $e\n$stackTrace'.logs();
  }
}

// ✅ NEW: Separate WorkManager initialization with proper error handling
Future<void> _initializeWorkManager() async {
  try {
    // Cancel all existing tasks first
    await Workmanager().cancelAll();

    // Small delay to ensure cleanup is complete
    await Future.delayed(Duration(milliseconds: 1000));

    // Initialize WorkManager
    await Workmanager().initialize(
      callbackDispatcher,
      isInDebugMode: false,
    );

    // Register periodic task with error handling
    await Workmanager().registerPeriodicTask(
      'backgroundSyncTask',
      'syncAttendanceAndLeave',
      frequency: const Duration(minutes: 15),
      constraints: Constraints(
        networkType: NetworkType.connected,
      ),
      existingWorkPolicy: ExistingWorkPolicy.replace,
      backoffPolicy: BackoffPolicy.exponential,
    );

    'WorkManager initialized successfully'.logs();
  } catch (e, stackTrace) {
    'WorkManager initialization failed: $e\n$stackTrace'.logs();
    // Continue app execution even if WorkManager fails
  }
}
}

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.