Skip to content

CRITICAL: Incorrect package content for v9.1.0 served from pub.dev - Verified in UAE and UK #8893

@wediabdat2-source

Description

@wediabdat2-source

We are experiencing a critical issue where pub.dev is serving incorrect package content for flutter_foreground_task version 9.1.0.

  • Initially, this was observed in Dubai, UAE. The downloaded flutter_foreground_task-9.1.0.tar.gz has an SHA-256 hash of 9F1B25A81DB95D7119D2C5CFFC654048CBDD49D4056183E1BEADC1A6A38F3E29.
  • This hash corresponds to code from an older version (appears to be v3.10.0 or similar), not v9.1.0, leading to API mismatches (e.g., missing printDevLog in init).
  • Crucially, a friend in the UK has also downloaded the package from pub.dev today ([8/12/2025]) and sent the file to me, and when I checked the SHA-256 hash it is the exact same incorrect SHA-256 hash: 9F1B25A81DB95D7119D2C5CFFC654048CBDD49D4056183E1BEADC1A6A38F3E29.
  • The expected SHA-256 hash for the correct v9.1.0 (verified by other developers in different regions previously, or by checking older, correctly cached versions if available) is F15A8501865048A092829281A18277C0879007808269A94018698991738D1847.
  • This indicates a widespread problem with the distribution of flutter_foreground_task v9.1.0 on pub.dev, potentially affecting multiple regions or even being a global issue with the uploaded package itself.
  • This prevents users from utilizing the correct version and is causing build failures. Please investigate urgently with the pub.dev team.
  • But if am wrong please inform me.

here is the source code inside the flutter_foreground_task.dart
"
import 'dart:async';
import 'dart:isolate';
import 'dart:ui';

import 'package:flutter/widgets.dart';
import 'package:shared_preferences/shared_preferences.dart';

import 'flutter_foreground_task_platform_interface.dart';
import 'errors/service_already_started_exception.dart';
import 'errors/service_not_initialized_exception.dart';
import 'errors/service_not_started_exception.dart';
import 'errors/service_timeout_exception.dart';
import 'models/foreground_service_types.dart';
import 'models/foreground_task_options.dart';
import 'models/notification_button.dart';
import 'models/notification_icon.dart';
import 'models/notification_options.dart';
import 'models/notification_permission.dart';
import 'models/service_request_result.dart';
import 'task_handler.dart';
import 'utils/utility.dart';

export 'errors/service_already_started_exception.dart';
export 'errors/service_not_initialized_exception.dart';
export 'errors/service_not_started_exception.dart';
export 'errors/service_timeout_exception.dart';
export 'models/foreground_service_types.dart';
export 'models/foreground_task_event_action.dart';
export 'models/foreground_task_options.dart';
export 'models/notification_button.dart';
export 'models/notification_channel_importance.dart';
export 'models/notification_icon.dart';
export 'models/notification_options.dart';
export 'models/notification_permission.dart';
export 'models/notification_priority.dart';
export 'models/notification_visibility.dart';
export 'models/service_request_result.dart';
export 'ui/with_foreground_task.dart';
export 'task_handler.dart';

const String _kPortName = 'flutter_foreground_task/isolateComPort';
const String _kPrefsKeyPrefix = 'com.pravera.flutter_foreground_task.prefs.';

typedef DataCallback = void Function(Object data);

/// A class that implements foreground task and provides useful utilities.
class FlutterForegroundTask {
// ====================== Service ======================

@VisibleForTesting
static AndroidNotificationOptions? androidNotificationOptions;

@VisibleForTesting
static IOSNotificationOptions? iosNotificationOptions;

@VisibleForTesting
static ForegroundTaskOptions? foregroundTaskOptions;

@VisibleForTesting
static bool isInitialized = false;

@VisibleForTesting
static bool skipServiceResponseCheck = false;

// platform instance: MethodChannelFlutterForegroundTask
static FlutterForegroundTaskPlatform get _platform =>
FlutterForegroundTaskPlatform.instance;

/// Resets class's static values to allow for testing of service flow.
@VisibleForTesting
static void resetStatic() {
androidNotificationOptions = null;
iosNotificationOptions = null;
foregroundTaskOptions = null;
isInitialized = false;
skipServiceResponseCheck = false;

receivePort?.close();
receivePort = null;
streamSubscription?.cancel();
streamSubscription = null;
dataCallbacks.clear();

}

/// Initialize the [FlutterForegroundTask].
static void init({
required AndroidNotificationOptions androidNotificationOptions,
required IOSNotificationOptions iosNotificationOptions,
required ForegroundTaskOptions foregroundTaskOptions,
}) {
FlutterForegroundTask.androidNotificationOptions =
androidNotificationOptions;
FlutterForegroundTask.iosNotificationOptions = iosNotificationOptions;
FlutterForegroundTask.foregroundTaskOptions = foregroundTaskOptions;
FlutterForegroundTask.isInitialized = true;
}

/// Start the foreground service.
static Future startService({
int? serviceId,
List? serviceTypes,
required String notificationTitle,
required String notificationText,
NotificationIcon? notificationIcon,
List? notificationButtons,
String? notificationInitialRoute,
Function? callback,
}) async {
try {
if (!isInitialized) {
throw ServiceNotInitializedException();
}

  if (await isRunningService) {
    throw ServiceAlreadyStartedException();
  }

  await _platform.startService(
    androidNotificationOptions: androidNotificationOptions!,
    iosNotificationOptions: iosNotificationOptions!,
    foregroundTaskOptions: foregroundTaskOptions!,
    serviceId: serviceId,
    serviceTypes: serviceTypes,
    notificationTitle: notificationTitle,
    notificationText: notificationText,
    notificationIcon: notificationIcon,
    notificationButtons: notificationButtons,
    notificationInitialRoute: notificationInitialRoute,
    callback: callback,
  );

  if (!skipServiceResponseCheck) {
    await checkServiceStateChange(target: true);
  }

  return const ServiceRequestSuccess();
} catch (error) {
  return ServiceRequestFailure(error: error);
}

}

/// Restart the foreground service.
static Future restartService() async {
try {
if (!(await isRunningService)) {
throw ServiceNotStartedException();
}

  await _platform.restartService();

  return const ServiceRequestSuccess();
} catch (error) {
  return ServiceRequestFailure(error: error);
}

}

/// Update the foreground service.
static Future updateService({
ForegroundTaskOptions? foregroundTaskOptions,
String? notificationTitle,
String? notificationText,
NotificationIcon? notificationIcon,
List? notificationButtons,
String? notificationInitialRoute,
Function? callback,
}) async {
try {
if (!(await isRunningService)) {
throw ServiceNotStartedException();
}

  await _platform.updateService(
    foregroundTaskOptions: foregroundTaskOptions,
    notificationText: notificationText,
    notificationTitle: notificationTitle,
    notificationIcon: notificationIcon,
    notificationButtons: notificationButtons,
    notificationInitialRoute: notificationInitialRoute,
    callback: callback,
  );

  return const ServiceRequestSuccess();
} catch (error) {
  return ServiceRequestFailure(error: error);
}

}

/// Stop the foreground service.
static Future stopService() async {
try {
if (!(await isRunningService)) {
throw ServiceNotStartedException();
}

  await _platform.stopService();

  if (!skipServiceResponseCheck) {
    await checkServiceStateChange(target: false);
  }

  return const ServiceRequestSuccess();
} catch (error) {
  return ServiceRequestFailure(error: error);
}

}

@VisibleForTesting
static Future checkServiceStateChange({required bool target}) async {
// official doc: Once the service has been created, the service must call its startForeground() method within 5 seconds.
// ref: https://developer.android.com/guide/components/services#StartingAService
final bool isCompleted = await Utility.instance.completedWithinDeadline(
deadline: const Duration(seconds: 5),
future: () async {
return target == await isRunningService;
},
);

if (!isCompleted) {
  throw ServiceTimeoutException();
}

}

/// Returns whether the foreground service is running.
static Future get isRunningService => _platform.isRunningService;

/// Set up the task handler and start the foreground task.
///
/// It must always be called from a top-level function, otherwise foreground task will not work.
static void setTaskHandler(TaskHandler handler) =>
_platform.setTaskHandler(handler);

// =================== Communication ===================

@VisibleForTesting
static ReceivePort? receivePort;

@VisibleForTesting
static StreamSubscription? streamSubscription;

@VisibleForTesting
static final List dataCallbacks = [];

/// Initialize port for communication between TaskHandler and UI.
static void initCommunicationPort() {
final ReceivePort newReceivePort = ReceivePort();
final SendPort newSendPort = newReceivePort.sendPort;

IsolateNameServer.removePortNameMapping(_kPortName);
if (IsolateNameServer.registerPortWithName(newSendPort, _kPortName)) {
  streamSubscription?.cancel();
  receivePort?.close();

  receivePort = newReceivePort;
  streamSubscription = receivePort?.listen((data) {
    for (final DataCallback callback in dataCallbacks.toList()) {
      callback.call(data);
    }
  });
}

}

/// Add a callback to receive data sent from the [TaskHandler].
static void addTaskDataCallback(DataCallback callback) {
if (!dataCallbacks.contains(callback)) {
dataCallbacks.add(callback);
}
}

/// Remove a callback to receive data sent from the [TaskHandler].
static void removeTaskDataCallback(DataCallback callback) {
dataCallbacks.remove(callback);
}

/// Send data to [TaskHandler].
static void sendDataToTask(Object data) => _platform.sendDataToTask(data);

/// Send date to main isolate.
static void sendDataToMain(Object data) {
final SendPort? sendPort = IsolateNameServer.lookupPortByName(_kPortName);
sendPort?.send(data);
}

// ====================== Storage ======================

/// Get the stored data with [key].
static Future<T?> getData({required String key}) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.reload();

final Object? data = prefs.get(_kPrefsKeyPrefix + key);

return (data is T) ? data : null;

}

/// Get all stored data.
static Future<Map<String, Object>> getAllData() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.reload();

final Map<String, Object> dataList = {};
for (final String prefsKey in prefs.getKeys()) {
  if (prefsKey.contains(_kPrefsKeyPrefix)) {
    final Object? data = prefs.get(prefsKey);
    if (data != null) {
      final String originKey = prefsKey.replaceAll(_kPrefsKeyPrefix, '');
      dataList[originKey] = data;
    }
  }
}

return dataList;

}

/// Save data with [key].
static Future saveData({
required String key,
required Object value,
}) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.reload();

final String prefsKey = _kPrefsKeyPrefix + key;
if (value is int) {
  return prefs.setInt(prefsKey, value);
} else if (value is double) {
  return prefs.setDouble(prefsKey, value);
} else if (value is String) {
  return prefs.setString(prefsKey, value);
} else if (value is bool) {
  return prefs.setBool(prefsKey, value);
} else {
  return false;
}

}

/// Remove data with [key].
static Future removeData({required String key}) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.reload();

return prefs.remove(_kPrefsKeyPrefix + key);

}

/// Clears all stored data.
static Future clearAllData() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.reload();

for (final String prefsKey in prefs.getKeys()) {
  if (prefsKey.contains(_kPrefsKeyPrefix)) {
    await prefs.remove(prefsKey);
  }
}

return true;

}

// ====================== Utility ======================

/// Minimize the app to the background.
static void minimizeApp() => _platform.minimizeApp();

/// Launch the app at [route] if it is not running otherwise open it.
///
/// It is also possible to pass a route to this function but the route will only
/// be loaded if the app is not already running.
///
/// This function requires the "android.permission.SYSTEM_ALERT_WINDOW" permission and
/// requires using the openSystemAlertWindowSettings() function to grant the permission.
static void launchApp([String? route]) => _platform.launchApp(route);

/// Toggles lockScreen visibility.
static void setOnLockScreenVisibility(bool isVisible) =>
_platform.setOnLockScreenVisibility(isVisible);

/// Returns whether the app is in the foreground.
static Future get isAppOnForeground => _platform.isAppOnForeground;

/// Wake up the screen of a device that is turned off.
static void wakeUpScreen() => _platform.wakeUpScreen();

/// Returns whether the app has been excluded from battery optimization.
static Future get isIgnoringBatteryOptimizations =>
_platform.isIgnoringBatteryOptimizations;

/// Open the settings page where you can set ignore battery optimization.
static Future openIgnoreBatteryOptimizationSettings() =>
_platform.openIgnoreBatteryOptimizationSettings();

/// Request to ignore battery optimization.
///
/// This function requires the "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" permission.
static Future requestIgnoreBatteryOptimization() =>
_platform.requestIgnoreBatteryOptimization();

/// Returns whether the "android.permission.SYSTEM_ALERT_WINDOW" permission is granted.
static Future get canDrawOverlays => _platform.canDrawOverlays;

/// Open the settings page where you can allow/deny the "android.permission.SYSTEM_ALERT_WINDOW" permission.
static Future openSystemAlertWindowSettings() =>
_platform.openSystemAlertWindowSettings();

/// Returns notification permission status.
static Future checkNotificationPermission() =>
_platform.checkNotificationPermission();

/// Request notification permission.
static Future requestNotificationPermission() =>
_platform.requestNotificationPermission();

/// Returns whether the "android.permission.SCHEDULE_EXACT_ALARM" permission is granted.
static Future get canScheduleExactAlarms =>
_platform.canScheduleExactAlarms;

/// Open the alarms & reminders settings page.
///
/// Use this utility only if you provide services that require long-term survival,
/// such as exact alarm service, healthcare service, or Bluetooth communication.
///
/// This utility requires the "android.permission.SCHEDULE_EXACT_ALARM" permission.
/// Using this permission may make app distribution difficult due to Google policy.
static Future openAlarmsAndRemindersSettings() =>
_platform.openAlarmsAndRemindersSettings();
}
"

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions