I am using flutter_background_service. I need to process image when the app goes in the background. I need some values for the processing that I store in shared preferences before the processing.
///
Future<void> initializeService() async {
final service = FlutterBackgroundService();
// Create notification channel for Android
const AndroidNotificationChannel channel = AndroidNotificationChannel(
notificationChannelId, // id
'IMAGE STAMPING SERVICE', // title
description: 'This channel is used for important notifications.',
importance: Importance.high, // Set importance
);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// Create the notification channel
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
await service.configure(
androidConfiguration: AndroidConfiguration(
onStart: onStart,
autoStart: true,
isForegroundMode: true,
notificationChannelId: notificationChannelId,
initialNotificationTitle: 'Image Stamping Service',
initialNotificationContent: 'Initializing',
foregroundServiceNotificationId: notificationId,
),
iosConfiguration: IosConfiguration(
autoStart: true,
onForeground: onStart,
),
);
}
/// Function to initialize photo watcher
void initializePhotoWatcher() async {
try {
// Initialize SharedPreferences
SharedPreferences prefs = await SharedPreferences.getInstance();
// Retrieve the value from SharedPreferences
bool arePermissionsGranted = prefs.getBool('permissionsGranted') ?? false;
if (arePermissionsGranted) {
photoDirectory = Directory(AppConstants.directoryPath);
if (await photoDirectory?.exists() ?? false) {
final watcher = DirectoryWatcher(photoDirectory!.path);
print("DirectoryWatcher created for path: ${photoDirectory!.path}");
watcher.events.listen((event) {
print("Event detected: ${event.toString()}");
if (event.type == ChangeType.ADD &&
!event.path.contains("stamped_") &&
!event.path.contains(".pending")) {
print("Scheduling processing for file: ${event.path}");
<------------ Invoking Service Here ------------>
final service = FlutterBackgroundService();
service.invoke('processImage', {'imagePath': event.path});
}
});
} else {
print("Camera directory does not exist or is not accessible");
}
} else {
// Request permissions and wait for them to be granted
bool permissionsGranted = await requestPermissions();
if (permissionsGranted) {
// Permissions were granted, continue with initialization
photoDirectory = Directory(AppConstants.directoryPath);
// Rest of the code...
} else {
// Permissions were not granted, handle accordingly
print("Need more permissions.");
}
}
} catch (e) {
print(e.toString());
}
}
///
Future<void> onStart(ServiceInstance service) async {
// DartPluginRegistrant.ensureInitialized();
// final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
// FlutterLocalNotificationsPlugin();
// Set up listeners for foreground and background state transitions
if (service is AndroidServiceInstance) {
service.on('processImage').listen((data) {
final String imagePath = data?['imagePath'];
service.setAsBackgroundService();
// Rest of the code to process the image
processImage(imagePath);
});
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
}
service.on('stopService').listen((event) {
service.stopSelf();
});
}
Retrieving in this:
void processImage(String imagePath) async {
try {
final File imageFile = File(imagePath);
final SharedPreferences prefs = await SharedPreferences.getInstance();
<------- Reloading as suggested in other solutions -------->
await prefs.reload();
// Retrieve user preferences
<------------ Everything takes the default value as the prefs return null ------------>
int fontSize = prefs.getInt('fontSize') ?? AppConstants.fontSize;
int scaledFontSize = fontSize * 2;
final Color fontColor = _getFontColorFromString(prefs);
// Retrieve text position
String textPositionString =
prefs.getString('textPlacement') ?? AppConstants.textPosition.name;
TextPosition textPosition = TextPosition.values.firstWhere(
(e) => e.name == textPositionString,
orElse: () => AppConstants.textPosition // default value if not found
);
String text = prefs.getString('text') ?? AppConstants.text;
// Check if the file exists and is not a temporary file
if (await imageFile.exists() && !imageFile.path.contains('.pending-')) {
// Call the function to add a stamp to the photo
await addStampToPhoto(
image: imageFile,
fontSize: scaledFontSize,
text: text,
fontColor: fontColor,
textPosition: textPosition,
photoDirectoryPath: AppConstants.directoryPath,
);
} else {
// Log and handle the situation, perhaps by retrying later
print('File does not exist or is a temporary file: $imagePath');
}
} //
catch (e) {
print('Error in processing file $imagePath: $e');
// Optionally implement a retry mechanism or other error handling
}
}
The core challenge lies in accessing SharedPreferences values across isolates when using flutter_background_service
. This limitation arises from background services running in separate isolates for efficiency and security.
So I suggest passing values as arguments.
like
service.invoke('processImage', {
'imagePath': event.path,
'fontSize': prefs.getInt('fontSize') ?? AppConstants.fontSize,
});
and onStart
function
Future<void> onStart(ServiceInstance service) async {
service.on('processImage').listen((data) {
final String imagePath = data?['imagePath'];
final int fontSize = data?['fontSize'];
processImage(imagePath, fontSize);
});
}