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
}
}
}