Closed yaroslav13 closed 2 years ago
pragma("vm:entry-point")
I’ve never heard of it. What is it???
@pragma("vm:entry-point") to mark a function (or other entities, as classes) to indicate to the compiler that it will be used from native code. Without this annotation, the dart compiler could strip out unused functions, inline them, shrink names, etc, and the native code would fail to call it.
A very good doc (written much more clearly than usual) about entry-point is https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md
I’ve never heard of it. Use it if you wish.
So... I am asking because firebase messaging adds for his docs this point for [_firebaseMessagingBackgroundHandler]. Maybe it is somehow related to the new Flutter version... I don't know (
It must be annotated with @pragma('vm:entry-point')
right above the function declaration (otherwise it may be removed during tree shaking for release mode).
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message)
Yes, I believe it's needed, but it didn't resolve the issue for me. When using the location service while my app is in the foreground, it works fine. Once swiped away, I get the following errors:
D/FlutterGeolocator(27763): Creating service.
D/FlutterGeolocator(27763): Binding to location service.
E/flutter (27763): [ERROR:flutter/shell/common/shell.cc(93)] Dart Error: Dart_LookupLibrary: library 'package:flutter_background_geolocation/flutter_background_geolocation.dart' not found.
E/flutter (27763): [ERROR:flutter/runtime/dart_isolate.cc(668)] Could not resolve main entrypoint function.
E/flutter (27763): [ERROR:flutter/runtime/dart_isolate.cc(167)] Could not run the run main Dart entrypoint.
E/flutter (27763): [ERROR:flutter/runtime/runtime_controller.cc(385)] Could not create root isolate.
E/flutter (27763): [ERROR:flutter/shell/common/shell.cc(604)] Could not launch engine with configuration.
I tried adding the decorator per the Dart guidance as such:
/// Receive events from BackgroundGeolocation in Headless state.
@pragma('vm:entry-point')
void backgroundGeolocationHeadlessTask(bg.HeadlessEvent headlessEvent) async {
and I am using the following version of Flutter
[√] Flutter (Channel stable, 3.3.3, on Microsoft Windows [Version 10.0.22621.521], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
but it's still not working. I am using 4.3.4
version of the plugin.
I suggest you attempt to reproduce this with the /example app in this repo.
Also, the issue template asked you for a number of important pieces of information, including flutter doctor
and plugin version.
I downgraded flutter to v3.0.5
in the meantime
flutter downgrade v3.0.5
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.3.4, on macOS 12.6 21G115 darwin-x64, locale en-CA)
I have no issue with headless on the /example
app in this repo using latest flutter sdk (3.3.4
). Have you tried the /example?
Show me your entire lib/main.dart
Show me your entire
lib/main.dart
@christocracy I am not sure if that's directed at me, but here you go:
// DART IMPORTS
import 'dart:async';
import 'dart:io' show Platform;
// PACKAGE IMPORTS
import 'package:background_fetch/background_fetch.dart';
// import 'package:connectivity/connectivity.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart'
as bg;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get_it/get_it.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wakelock/wakelock.dart';
// LOCAL IMPORTS
import 'package:ive_arrived/constants.dart';
import 'package:ive_arrived/controllers/connectivity_controller.dart';
import 'package:ive_arrived/controllers/location_controller.dart';
// import 'package:ive_arrived/controllers/notification_controller.dart';
import 'package:ive_arrived/data/state_data.dart';
import 'package:ive_arrived/utilities/log.dart';
import 'package:ive_arrived/widgets/dialogs/dialog_service.dart';
import 'package:ive_arrived/widgets/home_screen.dart';
import 'package:ive_arrived/widgets/new_version_screen.dart';
import 'package:ive_arrived/widgets/onboarding_screen.dart';
import 'package:ive_arrived/widgets/opening_splash_screen.dart';
ConnectivityController connectivityController = ConnectivityController();
StateData stateData = StateData();
GetIt getIt = GetIt.instance;
const fetchBackgroundLocation = "fetchBackgroundLocation";
bool permissionsGranted = false;
void main() {
WidgetsFlutterBinding.ensureInitialized();
// Initialize ads
MobileAds.instance.initialize();
// Set app mode (debug/release) into state data
stateData.setMode(!bool.fromEnvironment('dart.vm.product'));
// Get the logging enabled setting at build time to specify if we should
// output logs or not.
const bool loggingEnabled =
bool.fromEnvironment("logging-enabled", defaultValue: false);
bool isInDebugMode = stateData.isDebug();
bool logsEnabled = isInDebugMode || loggingEnabled;
print("Logs enabled? " + (logsEnabled ? "yes" : "no"));
stateData.setLoggingEnabled(logsEnabled);
// Set OS into state data
stateData.setAndroid(Platform.isAndroid);
stateData.toggleStateNotifications(isInDebugMode);
// Set the callback functions to be performed when we lose or restore
// connectivity for headless (will be overwritten in home screen later when
// headed)
connectivityController.setLostConnectionCallback(() async {
Log.logMessage(
"The app has lost connection to the internet! Main callback.");
stateData.setConnected(false);
});
connectivityController.setRestoredConnectionCallback(() async {
Log.logMessage("The app is connected to the internet! Main callback.");
stateData.setConnected(true);
});
ConnectivityController().isConnected();
// https://github.com/flutter/plugins/blob/master/packages/firebase_crashlytics/example/lib/main.dart
// Pass all uncaught errors to Crashlytics.
FlutterError.onError = (FlutterErrorDetails details) {
if (isInDebugMode) {
// In development mode, simply print to console.
Log.logError(details.exception.toString(), details.stack);
} else {
// In production mode, report to the application zone to report to
// Sentry.
Zone.current.handleUncaughtError(details.exception, details.stack);
}
};
getIt.registerLazySingleton(() => DialogService());
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings(assetLogoNotificationIcon);
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
onDidReceiveLocalNotification: onDidReceiveLocalNotification,
);
final MacOSInitializationSettings initializationSettingsMacOS =
MacOSInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false);
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
macOS: initializationSettingsMacOS);
// Initialize the tray notifications
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: selectNotification);
/// Application selection: Select the app to boot:
/// - AdvancedApp
/// - HelloWorldAp
/// - HomeApp
///
SharedPreferences.getInstance().then((SharedPreferences prefs) {
// Sanitize old-style registration system that only required username.
// If we find a valid username but null orgname, reverse them.
String orgname = prefs.getString("orgname");
String username = prefs.getString("username");
if (orgname == null && username != null) {
prefs.setString("orgname", username);
prefs.remove("username");
}
});
TransistorAuth.registerErrorHandler();
/// Register BackgroundGeolocation headless-task.
bg.BackgroundGeolocation.registerHeadlessTask(
backgroundGeolocationHeadlessTask);
runZonedGuarded<Future<void>>(() async {
runApp(IveArrived());
}, (dynamic exception, StackTrace stack, {dynamic context}) {
Log.logError("Crashlytics exception: $exception", stack);
//return Crashlytics.instance.recordError(exception, stack, context: context);
});
}
/// Return the notification mode based on the product flavor, defined by the app
/// package name. https://stackoverflow.com/a/61674385
///
/// @return Future<int> notifMode* constant enum based on package name
Future<int> getNotifMode() async {
// Flavor extraction
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String packageName = packageInfo.packageName;
switch (packageName) {
case "$dynamicLinkPackageNameBase$productFlavorSms":
return notifModeSms;
}
return notifModeTwilio;
}
/// Initialize the stateData object and global dynamicLinkPackageName variables
/// based on the product flavor.
Future<void> initNotifModeAndPackageName() async {
int notifMode = await getNotifMode();
switch (notifMode) {
case notifModeSms:
dynamicLinkPackageName += productFlavorSms;
break;
default:
dynamicLinkPackageName += productFlavorTwilio;
}
stateData.setNotifMode(notifMode);
}
/// Receive events from BackgroundGeolocation in Headless state.
@pragma('vm:entry-point', true)
void backgroundGeolocationHeadlessTask(bg.HeadlessEvent headlessEvent) async {
Log.logFunctionName(
"main", "backgroundGeolocationHeadlessTask($headlessEvent)");
// Set because we are headless
stateData.setHeadless(true);
// Init notification mode stateData info
await initNotifModeAndPackageName();
bg.Location location;
switch (headlessEvent.name) {
case bg.Event.TERMINATE:
try {
location =
await bg.BackgroundGeolocation.getCurrentPosition(samples: 1);
} catch (error) {
Log.logError("[getCurrentPosition] Headless ERROR: $error");
}
break;
case bg.Event.HEARTBEAT:
try {
location =
await bg.BackgroundGeolocation.getCurrentPosition(samples: 1);
} catch (error) {
Log.logError("[getCurrentPosition] Headless ERROR: $error");
}
break;
case bg.Event.LOCATION:
location = headlessEvent.event;
break;
case bg.Event.MOTIONCHANGE:
location = headlessEvent.event;
break;
case bg.Event.GEOFENCE:
bg.GeofenceEvent geofenceEvent = headlessEvent.event;
Log.logMessage(geofenceEvent);
break;
case bg.Event.GEOFENCESCHANGE:
bg.GeofencesChangeEvent event = headlessEvent.event;
Log.logMessage(event);
break;
case bg.Event.SCHEDULE:
bg.State state = headlessEvent.event;
Log.logMessage(state);
break;
case bg.Event.ACTIVITYCHANGE:
bg.ActivityChangeEvent event = headlessEvent.event;
Log.logMessage(event);
break;
case bg.Event.HTTP:
bg.HttpEvent response = headlessEvent.event;
Log.logMessage(response);
break;
case bg.Event.POWERSAVECHANGE:
bool enabled = headlessEvent.event;
Log.logMessage(enabled);
break;
case bg.Event.CONNECTIVITYCHANGE:
bg.ConnectivityChangeEvent event = headlessEvent.event;
Log.logMessage(event);
break;
case bg.Event.ENABLEDCHANGE:
bool enabled = headlessEvent.event;
Log.logMessage(enabled);
break;
case bg.Event.AUTHORIZATION:
bg.AuthorizationEvent event = headlessEvent.event;
Log.logMessage(event);
// bg.BackgroundGeolocation.setConfig(
// bg.Config(url: "${ENV.TRACKER_HOST}/api/locations"));
break;
}
// If we have a valid location that is moving, handle the event
if (location != null) {
Log.logMessage("Got a headless location: $location");
LocationController.handleEvents(
location.coords.latitude, location.coords.longitude);
}
}
/// Receive events from BackgroundFetch in Headless state.
void backgroundFetchHeadlessTask(String taskId) async {
Log.logFunctionName("main", "backgroundFetchHeadlessTask($taskId)");
SharedPreferences prefs = await SharedPreferences.getInstance();
int count = 0;
if (prefs.get("fetch-count") != null) {
count = prefs.getInt("fetch-count");
}
prefs.setInt("fetch-count", ++count);
Log.logMessage("[BackgroundFetch] count: $count");
BackgroundFetch.finish(taskId);
}
/// For flutter_background_geolocation
/// https://github.com/transistorsoft/flutter_background_geolocation/blob/master/example/lib/config/transistor_auth.dart
class TransistorAuth {
static Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
static Future<bool> register() async {
try {
SharedPreferences prefs = await _prefs;
// Request a JWT from tracker.transistorsoft.com
String orgname = prefs.getString("orgname");
String username = prefs.getString("username");
if (orgname == null || username == null) {
return false;
}
bg.TransistorAuthorizationToken jwt =
await bg.TransistorAuthorizationToken.findOrCreate(
orgname, username, ENV.TRACKER_HOST);
await bg.BackgroundGeolocation.setConfig(
bg.Config(transistorAuthorizationToken: jwt));
return true;
} catch (error) {
Log.logMessage("[ERROR] $error");
return false;
}
}
/// For flutter_background_geolocation
static Future<void> registerErrorHandler() async {
bg.State state = await bg.BackgroundGeolocation.state;
if ((state.params != null) && (state.params['device'] != null)) {
_migrateConfig();
}
}
static void _migrateConfig() async {
Log.logFunctionName("TransistorAuth", "_migrateConfig()");
await bg.TransistorAuthorizationToken.destroy(ENV.TRACKER_HOST);
// https://github.com/transistorsoft/flutter_background_geolocation/blob/4d83901958e9e96ae3d31511c7ffcc0a247e3657/lib/models/background_geolocation.dart#L189
bg.BackgroundGeolocation.reset(bg.Config(
backgroundPermissionRationale: bg.PermissionRationale(
title: textLocationAccessRequired,
message: textLocationPermissionsNeeded,
positiveAction: textAllowWhileInUse,
negativeAction: textCancel),
debug: false,
enableHeadless: true,
heartbeatInterval: configHeartbeatInterval,
logLevel: bg.Config.LOG_LEVEL_VERBOSE,
desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH,
distanceFilter: limitMinimumDistanceFilter,
stopOnTerminate: false,
startOnBoot: true,
// url: "${ENV.TRACKER_HOST}/api/locations",
params: {}));
}
}
/// Callback method for when a tray notification is clicked on
Future<dynamic> onDidReceiveLocalNotification(
int id, String title, String body, String payload) async {
Log.logFunctionName("_IveArrived",
"onDidReceiveLocalNotification($id, $title, $body, $payload)");
// TODO: Route to event's tab
}
/// Callback method for selecting a notification?
Future selectNotification(String payload) async {
Log.logFunctionName("_IveArried", "selectNotification($payload)");
if (payload != null) {
// TODO: Do something?
}
}
/// Main jumping off point
class IveArrived extends StatefulWidget {
@override
_IveArrived createState() => _IveArrived();
}
class _IveArrived extends State<IveArrived>
with SingleTickerProviderStateMixin {
// final Connectivity _connectivity = Connectivity();
// StreamSubscription<ConnectivityResult> _connectivitySubscription;
StateData stateData = StateData();
GlobalKey _keyMain = GlobalKey();
Timer _timerLink;
// Will set the main global key for sizing
_afterLayout(_) {
Log.logFunctionName("_IveArrived", "_afterLayout()");
stateData.setMainKey(_keyMain);
}
/// Update the current location temporarily. This is done because location
/// may be turned off, but we may stil want to get the coords when creating a
/// new place.
Future<void> updateCurrentLocation() async {
bg.Location location = await bg.BackgroundGeolocation.getCurrentPosition(
persist: true, // <-- do persist this location
desiredAccuracy: 0, // <-- desire best possible accuracy
timeout: 3, // <-- wait 30s before giving up.
samples: 1 // <-- sample 3 location before selecting best.
);
if (location != null) {
stateData.setLocation(
location.coords.latitude, location.coords.longitude);
}
}
@override
void initState() {
Log.logFunctionName("_IveArrived", "initState()");
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
super.initState();
Wakelock.toggle(
enable:
stateData.isDebug()); // Keep screen on/active to avoid lock screen
// Set the callback for updating the current location
stateData.setUpdateCurrentLocationCallback(updateCurrentLocation);
// flutter_background_geolocation
_initPlatformState();
}
/// BackgroundGeolocation initstate
/// https://github.com/transistorsoft/flutter_background_geolocation
/// | [onLocation] | Fired with each recorded [Location] |
/// | [onMotionChange] | Fired when the plugin changes state between *moving* / *stationary* |
/// | [onHttp] | Fired with each HTTP response from your server. (see [Config.url]). |
/// | [onActivityChange] | Fired with each change in device motion-activity. |
/// | [onProviderChange] | Fired after changes to device location-services configuration. |
/// | [onHeartbeat] | Periodic timed events. See [Config.heartbeatInterval]. iOS requires [Config.preventSuspend]. |
/// | [onGeofence] | Fired with each [Geofence] transition event (`ENTER, EXIT, DWELL`). |
/// | [onGeofencesChange] | Fired when the list of actively-monitored geofences changed. See [Config.geofenceProximityRadius]. |
/// | [onSchedule] | Fired for [Config.schedule] events. |
/// | [onConnectivityChange] | Fired when network-connectivity changes (connected / disconnected). |
/// | [onPowerSaveChange] | Fired when state of operating-system's "power-saving" feature is enabled / disabled. |
/// | [onEnabledChange] | Fired when the plugin is enabled / disabled via its [start] / [stop] methods. |
/// | [onNotificationAction]
Future<Null> _initPlatformState() async {
Log.logFunctionName("_IveArrived", "_initPlatformState()");
// Init notification mode stateData info
await initNotifModeAndPackageName();
bg.BackgroundGeolocation.onActivityChange((bg.ActivityChangeEvent event) {
Log.logMessage("[onActivityChange] $event");
});
bg.BackgroundGeolocation.onHeartbeat((bg.HeartbeatEvent event) {
Log.logMessage("*** heartbeat event received");
bg.BackgroundGeolocation.getCurrentPosition(
persist: true, // <-- do persist this location
desiredAccuracy: 0, // <-- desire best possible accuracy
timeout: 30000, // <-- wait 30s before giving up.
samples: 3 // <-- sample 3 location before selecting best.
)
.then((bg.Location location) {
Log.logMessage(
"BackgroundGeolocation.getCurrentPosition() - $location");
LocationController.handleEvents(
location.coords.latitude, location.coords.longitude);
}).catchError((error) {
Log.logError(
"BackgroundGeolocation.getCurrentPosition() ERROR: $error");
});
});
// bg.BackgroundGeolocation.onHttp((bg.HttpEvent event) async {
// Log.logMessage("[${bg.Event.HTTP}] - $event");
// });
bg.BackgroundGeolocation.onLocation((bg.Location location) {
Log.logMessage("In bg.BackgroundGeoLocation.onLocation(): $location");
LocationController.handleEvents(
location.coords.latitude, location.coords.longitude);
}, _onLocationError);
// Fired whenever the plugin changes motion-state (stationary->moving and vice-versa)
bg.BackgroundGeolocation.onMotionChange((bg.Location location) {
Log.logMessage("[motionchange] - $location");
LocationController.handleEvents(
location.coords.latitude, location.coords.longitude);
});
// Fired whenever the state of location-services changes. Always fired at boot
bg.BackgroundGeolocation.onProviderChange((bg.ProviderChangeEvent event) {
Log.logMessage("[providerchange] - $event");
});
}
void _onLocationError(bg.LocationError error) {
Log.logMessage("[location] ERROR - $error");
}
@override
void dispose() {
// _connectivitySubscription.cancel();
super.dispose();
if (_timerLink != null) {
_timerLink.cancel();
}
}
@override
Widget build(BuildContext context) {
// Force portrait orientation
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
// Hide bottom navbar and top bar ([]) for fullscreen mode
// Otherwse (SystemUiOverlay.values)
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
return MaterialApp(
key: _keyMain,
title: appTitle,
theme: ThemeData(
brightness: Brightness.light,
cardColor:
stateData.theme.backgroundColor, // For kebab menu background color
primaryColor: Colors.blue,
unselectedWidgetColor: Colors.red,
),
darkTheme: ThemeData(
brightness: Brightness.dark,
cardColor: Colors.black, // For kebab menu background color
unselectedWidgetColor: Colors.red),
initialRoute: routeOpeningSplashScreen,
routes: <String, WidgetBuilder>{
routeHomeScreen: (BuildContext context) => HomeScreen(),
routeNewVersionScreen: (BuildContext context) => NewVersionScreen(),
routeOnboardingScreen: (BuildContext context) => OnboardingScreen(),
routeOpeningSplashScreen: (BuildContext context) =>
OpeningSplashScreen(),
},
);
}
}
typedef MultiTapButtonCallback = void Function(bool correctNumberOfTouches);
I suggest you move the definition of void backgroundGeolocationHeadlessTask(bg.HeadlessEvent headlessEvent) async {
before main()
.
Have you tried experimenting with the plugin in a simple Hello World app?
I suggest you move the definition of
void backgroundGeolocationHeadlessTask(bg.HeadlessEvent headlessEvent) async {
beforemain()
.Have you tried experimenting with the plugin in a simple Hello World app?
I made the update that you proposed, but haven't updated Flutter version yet. Also I haven't tried the Hello World plugin for a while (when I started developing it was the basis, but once I had gotten things working, there was no need for it). Of course, Flutter changes, Dart changes, your plugin changes... :-) Things sometimes stop working for unexpected reasons.
An unrelated question: When using a GPS mock location app such as this one, I am finding that GeoLocation plugin doesn't seem to detect the change and trigger location change events with the GPS changes alone. Is there some trick you'd suggest for "forcing" the plugin to operate on the GPS changes for testing (vs being more battery efficient using accelerometers, etc)?
GeoLocation plugin doesn't seem to detect the change and trigger location change events with the GPS changes alone
That's correct. The plugin only turns on location-services and begins listening to location updates when the device is detected to be moving or exits an internal geofence around the last known position.
You have to manually engage the moving state by calling .changePace(true)
.
You have to manually engage the moving state by calling .changePace(true).
Yep! That does it! :-) Thank you.
Is this issue solved?
Is this issue solved?
No, it's not. I upgraded flutter to 3.3.5 to test if the pragma
addition and moving my handler above main()
fixed running in the background, but it's still producing the same error as soon as the headless process kicks in:
D/FlutterGeolocator(30004): Creating service. D/FlutterGeolocator(30004): Binding to location service. E/flutter (30004): [ERROR:flutter/shell/common/shell.cc(93)] Dart Error: Dart_LookupLibrary: library 'package:flutter_background_geolocation/flutter_background_geolocation.dart' not found. E/flutter (30004): [ERROR:flutter/runtime/dart_isolate.cc(668)] Could not resolve main entrypoint function. E/flutter (30004): [ERROR:flutter/runtime/dart_isolate.cc(167)] Could not run the run main Dart entrypoint. E/flutter (30004): [ERROR:flutter/runtime/runtime_controller.cc(385)] Could not create root isolate. E/flutter (30004): [ERROR:flutter/shell/common/shell.cc(604)] Could not launch engine with configuration.
I have no idea what could cause flutter to fail loading a dependency from pubspec.yaml
The plugin also works in profile mode and does not work in release mode, i tried all the methods and it didn't work, but I found that someone talked about Remove --fast -start ,so what does that mean please? it's working with firebase withe same issue (Dart Error: Dart_LookupLibrary).
The plugin is not responsible for being found by Dart_LookupLibrary
. I have no idea what could possibly be wrong, but the issue is certainly within your own environment.
Can confirm that headlessMode is indeed broken since Flutter v3.x.x. If I add @pragma('vm:entry-point')
above the BackgroundGeolocation
class inside the background_geolocator.dart
, this particular error (Dart Error: Dart_LookupLibrary: library
) is resolved, but then I get:
E/flutter (25032): [ERROR:flutter/runtime/dart_isolate.cc(668)] Could not resolve main entrypoint function.
E/flutter (25032): [ERROR:flutter/runtime/dart_isolate.cc(168)] Could not run the run main Dart entrypoint.
E/flutter (25032): [ERROR:flutter/runtime/runtime_controller.cc(390)] Could not create root isolate.
E/flutter (25032): [ERROR:flutter/shell/common/shell.cc(606)] Could not launch engine with configuration.
Please let us know how to proceed as I need this in production as soon as possible!
Create for me a simple HelloWorld app which reproduces the issue. Post it to a public Github repo and share that repo here with me.
It turned out that if I add @pragma('vm:entry-point')
above the _headlessCallbackDispatcher
in background_geolocator.dart
everything started working. It has to do with new tree shaking (as also mentioned here as well).
You can easily reproduce the issue by running a release build with the latest flutter from stable channel (3.3.5 - d9111f6402). If you'd like I can still create a simple HelloWorld app, but I'm almost 100% sure that's the case, as it started working after annotating everything that is called from native code with @pragma('vm:entry-point')
.
It also worked with background_fetch.dart Thank you so much for the solution.
I have produced this. I will release a point-release soon for both background_fetch
and background_geolocation
.
Fix is released:
flutter_background_fetch @ 4.8.3
background_fetch @ 1.1.2
Confirmed fixed with flutter 3.3.6. Thanks! ❤️
@christocracy Do we need to annotate geolocationHeadlessTask with pragma("vm:entry-point") ???