I want to update user's location in background.
To perform tasks in background, i used this package, workmanager.
Link to that: https://pub.dev/packages/workmanager
But i can not update the location, it seems like it can't work with async code?
here is my code,
void callbackDispatcher() {
Workmanager.executeTask((task, inputData) async {
await _HomeScreenState().updateUserLoc();
print('it is working?');
return Future.value(true);
});
}
//Inside my stateFul Widget,
void initState() {
super.initState();
Workmanager.initialize(
callbackDispatcher,
isInDebugMode: true,
);
Workmanager.registerPeriodicTask(
"1",
fetchBackground,
frequency: Duration(minutes: 15),
);
}
updateUserLoc() async {
print("executi9ng the updateUserLocccccc");
await getusersLocation();
print("executi9ng the updateUserLoc");
GeoFirePoint point = geo.point(latitude: lat, longitude: long);
_firestore.collection('locations').document(widget.userid).setData(
{'position': point.data},
merge: true,
);
}
Is there any other way of updating the users's location in background?
You have to call the call the code inside the updateUserLoc() directly inside the callbackDispatcher. Just make the callbackDispatcher async function.
Related
I have an app based on flutter and created a Home screen widget for android (with home_widget) showing information from the app. With flutter background_fetch I update these information regularly, which works fine. Now when I restart my phone (emulator or real device), the background_fetch task does not continue, despite headless: true and stopOnTerminate: false set. Instead the old information from the latest fetch before the restart are displayed in the widget again.
main.dart
import 'package:home_widget/home_widget.dart';
import 'package:background_fetch/background_fetch.dart';
import 'package:logging_to_logcat/logging_to_logcat.dart';
import 'package:logging/logging.dart';
void main() {
runApp(
const MaterialApp(
home: MyApp()
)
);
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
}
// [Android-only] This "Headless Task" is run when the Android app is terminated with `enableHeadless: true`
// Be sure to annotate your callback function to avoid issues in release mode on Flutter >= 3.3.0
#pragma('vm:entry-point')
void backgroundFetchHeadlessTask(HeadlessTask task) async {
String taskId = task.taskId;
bool isTimeout = task.timeout;
if (isTimeout) {
// This task has exceeded its allowed running-time.
// You must stop what you're doing and immediately .finish(taskId)
debugPrint("[BackgroundFetch] Headless task timed-out: $taskId");
BackgroundFetch.finish(taskId);
return;
}
HomeWidget.saveWidgetData('refresh_date', "restarted");
HomeWidget.updateWidget(name: 'WidgetLarge', iOSName: 'WidgetLarge');
debugPrint('[BackgroundFetch] Headless event received.');
BackgroundFetch.finish(taskId);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State createState() {
return MainPage();
}
}
class MainPage extends State {
#override
void initState() {
super.initState();
initPlatformState();
BackgroundFetch.start().then((int status) {
debugPrint('[BackgroundFetch] start success: $status');
}).catchError((e) {
debugPrint('[BackgroundFetch] start FAILURE: $e');
});
HomeWidget.saveWidgetData('refresh_date', "test2");
HomeWidget.updateWidget(name: 'WidgetLarge', iOSName: 'WidgetLarge');
}
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
// Configure BackgroundFetch.
int status = await BackgroundFetch.configure(BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
requiresBatteryNotLow: false,
requiresCharging: false,
requiresStorageNotLow: false,
requiresDeviceIdle: false,
requiredNetworkType: NetworkType.ANY,
startOnBoot: true,
forceAlarmManager: true
), (String taskId) async { // <-- Event handler
// This is the fetch-event callback.
print("[BackgroundFetch] Event received $taskId");
setState(() {
latestUpdate = DateTime.now();
HomeWidget.saveWidgetData('refresh_date', "test");
HomeWidget.updateWidget(name: 'WidgetLarge', iOSName: 'WidgetLarge');
});
// IMPORTANT: You must signal completion of your task or the OS can punish your app
// for taking too long in the background.
BackgroundFetch.finish(taskId);
}, (String taskId) async { // <-- Task timeout handler.
// This task has exceeded its allowed running-time. You must stop what you're doing and immediately .finish(taskId)
debugPrint("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
BackgroundFetch.finish(taskId);
});
debugPrint('[BackgroundFetch] configure success: $status');
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
}
}
I import background_fetch like this:
dependencies:
...
home_widget: ^0.1.6
background_fetch:
git:
url: https://github.com/transistorsoft/flutter_background_fetch
I just updated flutter to the latest version with flutter upgrade and now it's working. Even tough the headless task begins executing 15 minutes after the reboot, so I still try to figure out how do execute it immediately after the reboot.
so i'm creating an app like facebook,instagram ....
When i was in systeme notifications step, i managed to display notifications when the App state is Open or paused but not when the app is dettached (closed), i'm no longer able to recieve new local notifications.
i tried many packages like flutter_background_service and workmanager, i'm working with flutter_background_service now in debug mode all was good, but when i build the apk it's not working in background.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
AwesomeNotifications().initialize('resource://drawable/appicon', [
NotificationChannel(
channelKey: 'basic_channel',
channelName: 'basic Notifications',
channelDescription: "channelDescription",
importance: NotificationImportance.High,
enableVibration: true,
channelShowBadge: true,
)
]);
await Firebase.initializeApp(
name: 'firebase',
options: const FirebaseOptions(//firebaseconfig),
);
await initializeService();
runApp(MyApp());
}
Future<void> initializeService() async {
final service = FlutterBackgroundService();
await service.configure(
androidConfiguration: AndroidConfiguration(
// this will be executed when app is in foreground or background in separated isolate
// auto start service
autoStart: true,
onStart: onStart,
isForegroundMode: false,
),
iosConfiguration: IosConfiguration(
// auto start service
autoStart: true,
// this will be executed when app is in foreground in separated isolate
onForeground: onStart,
// you have to enable background fetch capability on xcode project
onBackground: onIosBackground,
),
);
service.startService();
}
// to ensure this is executed
// run app from xcode, then from xcode menu, select Simulate Background Fetch
bool onIosBackground(ServiceInstance service) {
WidgetsFlutterBinding.ensureInitialized();
print('FLUTTER BACKGROUND FETCH');
return true;
}
void onStart(ServiceInstance service) async {
DartPluginRegistrant.ensureInitialized();
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
}
service.on('stopService').listen((event) {
service.stopSelf();
});
// bring to foreground
Timer.periodic(const Duration(seconds: 1), (timer) async {
/// you can see this log in logcat
print('FLUTTER BACKGROUND SERVICE is running : ${DateTime.now()}');
//////////////////////////////////////////////////////////////////////////////////////////
I want somthing like this: keep 2 getxcontroller working even if the app detached
//////////////////////////////////////////////////////////////////////////////////////////
await Firebase.initializeApp(
name: 'firebase',
options: const FirebaseOptions(//firebaseconfig),
).whenComplete(() {
final notifc = Get.put(Notificationsc());
final msgl = Get.put(MessageslistsController());
});
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
if (service is AndroidServiceInstance) {
service.setForegroundNotificationInfo(
title: "My App Service",
content: "Updated at ${DateTime.now()}",
);
}
// test using external plugin
final deviceInfo = DeviceInfoPlugin();
String? device;
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
device = androidInfo.model;
}
if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
device = iosInfo.model;
}
service.invoke(
'update',
{
"current_date": DateTime.now().toIso8601String(),
"device": device,
},
);
});
}
I am having an issue when work managers scheduler runs it gets stuck on reading data from database:
Future<List<TimesheetDays>> getTimesheetDays() async{
print('Getting DB');
Database db = await instance.database;
print('Getting DB stuff');
var timesheetday = await db.query('TimesheetDays',orderBy: 'TimesheetDayId');
// print(timesheetday);
List<TimesheetDays> timesheetDaysList = timesheetday.isNotEmpty ?
timesheetday.map((e) => TimesheetDays.fromMap(e)).toList() : [];
return timesheetDaysList;
}
It gets stuck on the
await instance.database
part of the code, now strange thing is that I have this project set on PC and Laptop. It only fails to work on PC whereas its fine on Laptop.
Work Manager code in case:
Callback Dispatcher
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
print('Executed scheduler!!');
try {
print('entered try catch!');
await CustomHttpRequests().synchronizeTimesheetDataLocally();
}
catch(_){
print('Error during execution');
}
print('Completed synchronization');
return Future.value(true);
});
}
Rest of the code:
Future<void> main() async{
WidgetsFlutterBinding.ensureInitialized();
print('creating task manager');
await Workmanager().initialize(
callbackDispatcher, // The top level function, aka callbackDispatcher
isInDebugMode: true, // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
);
Workmanager().registerPeriodicTask("1", fetchBackground, frequency: Duration(minutes: 15),initialDelay: Duration(minutes: 2)); //Android only (see below)
print('Task manager created');
runApp(
MultiProvider(providers: [
ChangeNotifierProvider(create: (_) => CoreUser())
],
child: MyApp(),)
);
}
And just so the call in dispatcher makes sense it references straight to database in its first line so this is where it fails:
Future synchronizeTimesheetDataLocally() async{
//await synchronizeWasteGradings();
print("started reading");
var timesheetData = await DatabaseHelper.instance.getTimesheetDays();
Some things I tried to do to fix it:
"flutter clean"
Made sure MainActivity and GeneratedPluginRegistrant are the same on both devices
(sqflite & work manager are both being registered properly)
Basically went through this:
https://github.com/tekartik/sqflite/blob/master/sqflite/doc/troubleshooting.md
Any ideas how to fix this?
I'm trying to run a statment with await inside a setState block, I added it inside another Future<void> function but still I need to add async on the setState to be able to run it .
here is my code :
setState(() async {
chosenStations.clear();
chosenStations.add(allStations[
suggestions.indexOf(fromLocationName)]);
_loading = true;
chosenStations.add(allStations[
suggestions.indexOf(toLocationName)]);
await showingLines();
});
Future<void> showingLines() async {
theLines.clear();
theLines = await DatabaseServices().fetchingLinesData(
chosenStations[0], chosenStations[1]);
}
and I got this error :
Instead of performing asynchronous work inside a call to setState(), first execute the work (without updating the widget state), and then synchronously update the state inside a call to setState().
The error tells that you need to move all the asynchronous logic out of setState because setState used to update the UI after doing some work apart of its nature
so what you can do is to move the showingLines function out of setState and await it then update the UI with the new lines
await showingLines();
setState(() {
chosenStations.clear();
chosenStations.add(allStations[
suggestions.indexOf(fromLocationName)]);
_loading = true;
chosenStations.add(allStations[
suggestions.indexOf(toLocationName)]);
});
Future<void> showingLines() async {
theLines.clear();
theLines = await DatabaseServices().fetchingLinesData(
chosenStations[0], chosenStations[1]);
}
Note: you can use setState directly without filling it with any work,
await showingLines();
chosenStations.clear();
chosenStations.add(allStations[
suggestions.indexOf(fromLocationName)]);
_loading = true;
chosenStations.add(allStations[
suggestions.indexOf(toLocationName)]);
setState(() {});
Future<void> showingLines() async {
theLines.clear();
theLines = await DatabaseServices().fetchingLinesData(
chosenStations[0], chosenStations[1]);
}
I have a text field, which has the onChange property, when it detects that the text has a \ n execute a function, the problem is that this function is executed twice, it should be mentioned, that in that function, I clean the text of the TextController.
TextField(
maxLines: null,
controller: codigoController,
autofocus: true,
onChanged: (text) {
if (text.contains('\n')) {
test();
}
},
),
_test() {
print("hello");
codigoController.clear();
}
One of the solution is to use listener on TextController
1. Add Listener
we can add listener at the first time Screen is rendered. Later we need to dispose it as stated by docs
class _AutoCallApiState extends State<AutoCallApi> {
TextEditingController codigoController = TextEditingController();
#override
void initState() {
super.initState();
codigoController.addListener(changesOnField);
}
#override
void dispose() {
codigoController.dispose(); // release unused memory in RAM
super.dispose();
}
2. Handle changes and API Call
Future callApi(String textToSent) async {
await Future.delayed(Duration(seconds: 5));
print("Received OK from API");
codigoController.clear();
}
void changesOnField() {
print("Changes Called");
String text = codigoController.text;
if (text.isNotEmpty) {
print('$text');
if (text.contains('\n')) {
callApi(text);
}
}
}
3. Demo
callApi method only called once
Note : you may see at the demo, it only prints "Saved data to API" once
4. Full Repo
You may look into this repo and build it locally. Github
Maybe you can try this
SchedulerBinding.instance.addPostFrameCallback((it) => {_controller.clear()});