How to make Flutter Workmanager plugin and Location plugin work together
Solution 1
I haven't tried what background location tracking but considering you only want location tracking from background then I think workmanager + geolocater would work. You should see this project ha_client it's using workmanager and geolocater together to get the coords
Also the location package you're using has it's own background location tracking which is in experimental Background Location Updates.
Solution 2
With these two plugins, you could schedule a one-off task and pass the current location into it.
be.tramckrijte.workmanager
has limitations (it even states that not everything is being supported). And combining these two plugins through Dart might lead nowhere, because the application is not always running. Even if there is a simple_callback_dispatcher_registration.dart, which shows how it works, this will call back to the Flutter engine and not query the other plugin.
Writing & scheduling a location-aware ListenableWorker
might be the best option available to get a GPS fix while running a task - unless there is any way to query the LocationPlugin
plugin through the Flutter engine, while running in the background. I mean, if the ListenableWorker
is location aware, it does not need to obtain the location elsewhere. Threading in WorkManager literally states it:
ListenableWorker
is the base class forWorker
,CoroutineWorker
, andRxWorker
. It is intended for Java developers who have to interact with callback-based asynchronous APIs such asFusedLocationProviderClient
...
Therefore ListenableWorker
, Worker
and CoroutineWorker
would be applicable classes to extend from. The equivalent for the iOS implementation would be a location-aware background service. The Flutter engine may start/stop them, provide start parameters or receive callbacks, but the code which is running in the background usually won't be Dart, since it should run independently.
Tom O
Updated on December 17, 2022Comments
-
Tom O over 1 year
1. Problem description
My goal is to build a Flutter app that gets periodic location updates using this workmanager plugin and using this location plugin. But I can't get the Location plugin to be loaded properly when my Workmanager callback fires. I get this error:
MissingPluginException(No implementation found for method getLocation on channel lyokone/location)
So basically, the problem is that when the Workmanager plugin tries to run dart code, it doesn't load up the Location plugin.
2. Other resources I have researched
I found others facing the same issue, here, here, and here.
As far as I understand, the solution provided to these questions boils down to: create a file named CustomApplication.java, which extends FlutterApplication, and which registers your plugin(s). And then register the CustomApplication.java file inside you AndoidManifest.xml file.
3. My code thus far
I have tried to make a bare-minimum app that implements the features I require:
- I implemented Workmanager plugin (works fine)
- I implemented Location plugin (works fine)
- I attempted to combine these features (does not work)
To see exactly what I have done at each step, please look here: https://gitlab.com/tomoerlemans/workmanager_with_location/-/commits/master. (This repository can also be used to quickly replicate the issue).
The relevant code files are as follows:
main.dart
import 'package:flutter/material.dart'; import 'package:workmanager/workmanager.dart'; import 'package:location/location.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); Workmanager.initialize(callbackDispatcher, isInDebugMode: true); runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ RaisedButton( onPressed: () { Workmanager.registerPeriodicTask( "1", "simpleTask"); }, child: Text("Start workmanager"), ), RaisedButton( onPressed: () { getLocation(); }, child: Text("Get current location"), ), ], ), ), ); } } void callbackDispatcher() { Workmanager.executeTask((task, inputData) { print("Native called background task at ${DateTime.now().toString()}"); getLocation(); return Future.value(true); }); } void getLocation() async { LocationData currentLocation; var location = new Location(); try { currentLocation = await location.getLocation(); } on Exception catch (e) { print("Error obtaining location: $e"); currentLocation = null; } print("Location altitude: ${currentLocation.altitude}"); print("Location longitude: ${currentLocation.longitude}"); }
pubspec.yaml
name: background_location description: A new Flutter project. version: 1.0.0+1 environment: sdk: ">=2.1.0 <3.0.0" dependencies: flutter: sdk: flutter cupertino_icons: ^0.1.2 workmanager: ^0.2.0 location: ^2.3.5 dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true
CustomApplication.java
package io.flutter.plugins; import io.flutter.app.FlutterApplication; import io.flutter.plugin.common.PluginRegistry; import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback; import io.flutter.plugins.GeneratedPluginRegistrant; import be.tramckrijte.workmanager.WorkmanagerPlugin; import com.lyokone.location.LocationPlugin; public class CustomApplication extends FlutterApplication implements PluginRegistry.PluginRegistrantCallback { @Override public void onCreate() { super.onCreate(); WorkmanagerPlugin.setPluginRegistrantCallback(this); } @Override public void registerWith(PluginRegistry registry) { WorkmanagerPlugin.registerWith(registry.registrarFor("be.tramckrijte.workmanager.WorkmanagerPlugin")); LocationPlugin.registerWith(registry.registrarFor("com.lyokone.location.LocationPlugin")); } }
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.background_location"> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <application android:name="io.flutter.plugins.CustomApplication" android:label="background_location" android:icon="@mipmap/ic_launcher"> <activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <meta-data android:name="flutterEmbedding" android:value="2" /> </application> </manifest>
Finally, I am running the following versions of Dart/Flutter/etc:
Flutter 1.12.13+hotfix.5 • channel stable • https://github.com/flutter/flutter.git Framework • revision 27321ebbad (10 weeks ago) • 2019-12-10 18:15:01 -0800 Engine • revision 2994f7e1e6 Tools • Dart 2.7.0
-
Tom O over 4 years"it even states that not everything is being supported" is kind of a blanket statement.. Can you tell me where it states that the feature I am looking for is impossible?
-
Martin Zeitler over 4 yearsThis way of thinking is kinda absurd; this is just alike pretending some imaginary entity would exist, only because one cannot proof it does not exist - the
WorkManager
documentation is pretty clear, like it or not. And you probably not fully comprehend what Flutter actually is, if assuming that the abstraction would be responsible for whatever native or JVM implementation (else there wouldn't be anandroid
and anios
directory)... all this plugin does is scheduling worker tasks by their name and eventually receive a callback. -
Martin Zeitler over 4 yearsA one-off task is no problem, because the abstraction-layer can directly pass lat/lng - but with a periodic task, you might in best case have the lat/lng of the moment when the worker task had been scheduled. And anybody experienced with
WorkManager
might suggest a location-awareWorker
- the only alternative might beflutter_geofire
, which eventually also has limitations, while the application is not running. -
Tom O over 4 yearsOne of the main contributors to the workmanager plugin repo, actually talks about running flutter plugins from the workmanager callback: github.com/vrtdev/flutter_workmanager/issues/6. I referenced this in my original question.