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.
Related
Hi I want my android app work in background even if the program completely close(terminate). I use the flutter_background_fetch for it. the notification show every 15 min correctly in app is open or go to background, But when I terminate app the background_fetch not work and not show the notification or do anything anymore.
here this my main file config:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
initializeNotificationService();
runApp(const MyApp());
}
#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)
BackgroundFetch.finish(taskId);
return;
}
// Do your work here...
BackgroundFetch.finish(taskId);
}
and this is part of my controller body for background_fetch:
class Controller extends GetxController {
#override
void onInit() {
// TODO: implement onInit
super.onInit();
startListeningNotificationEvents();
initPlatformState();
}
Future<void> initPlatformState() async {
var status = await BackgroundFetch.configure(
BackgroundFetchConfig(
minimumFetchInterval: 15,
forceAlarmManager: false,
stopOnTerminate: false,
startOnBoot: true,
enableHeadless: true,
requiresBatteryNotLow: false,
requiresCharging: false,
requiresStorageNotLow: false,
requiresDeviceIdle: false,
requiredNetworkType: NetworkType.NONE,
),
_onBackgroundFetch,
_onBackgroundFetchTimeout);
BackgroundFetch.scheduleTask(TaskConfig(
taskId: "test",
delay: 1000,
periodic: false,
stopOnTerminate: false,
enableHeadless: true));
BackgroundFetch.start();
}
void _onBackgroundFetchTimeout(String taskId) {
//BackgroundFetch.finish(taskId);
}
void _onBackgroundFetch(String taskId) async {
if (taskId == "test") {
random();
createNotification();
}
}
}
in the document said if we want app work even after terminate, we should set the "stopOnTerminate" equals false. but still not working. how can fix this? please help.
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,
},
);
});
}
import BackgroundService from 'react-native-background-actions';
const sleep = (time) => new Promise((resolve) => setTimeout(() => resolve(), time));
// You can do anything in your task such as network requests, timers and so on,
// as long as it doesn't touch UI. Once your task completes (i.e. the promise is resolved),
// React Native will go into "paused" mode (unless there are other tasks running,
// or there is a foreground app).
const veryIntensiveTask = async (taskDataArguments) => {
// Example of an infinite loop task
const { delay } = taskDataArguments;
await new Promise( async (resolve) => {
for (let i = 0; BackgroundService.isRunning(); i++) {
console.log(i);
await sleep(delay);
}
});
};
const options = {
taskName: 'Example',
taskTitle: 'ExampleTask title',
taskDesc: 'ExampleTask description',
taskIcon: {
name: 'ic_launcher',
type: 'mipmap',
},
color: '#ff00ff',
linkingURI: 'yourSchemeHere://chat/jane', // See Deep Linking for more info
parameters: {
delay: 1000,
},
};
await BackgroundService.start(veryIntensiveTask, options);
await BackgroundService.updateNotification({taskDesc: 'New ExampleTask description'}); // Only Android, iOS will ignore this call
// iOS will also run everything here in the background until .stop() is called
await BackgroundService.stop();
I am using react-native-background-actions. I need 2 buttons in this. But I don’t have any good experience with native code in react native so let me know how can I add custom buttons In it and changes buttons according condition. How can I make changes in the native android for notification buttons.
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 am new to Flutter Driver testing, and I have an issue that the tests always time out (in 30 seconds) while waiting for widgets to appear. My main class is only checking whether the Firebase user is not null. If a user is logged in, it is showing a dashboard, otherwise a login screen. While running the check, it is displaying a SplashScreen. The test "check flutter driver health" completes normally.
I tried find.byValueKey("auth_screen") instead of find.byType("AuthScreen"), it gives the same problem.
Error log:
VMServiceFlutterDriver: Connected to Flutter application.
00:01 +0: rendin app check flutter driver health
HealthStatus.ok
00:01 +1: rendin app Check login screen widgets
Splash screen
VMServiceFlutterDriver: waitFor message is taking a long time to complete...
VMServiceFlutterDriver: waitFor message is taking a long time to complete...
00:31 +1 -1: rendin app Check login screen widgets [E]
TimeoutException after 0:00:30.000000: Test timed out after 30 seconds.
Bad state: The client closed with pending request "ext.flutter.driver".
Here is my test code:
import 'package:test/test.dart';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
void main() {
group('app', () {
FlutterDriver driver;
// Connect to the Flutter driver before running any tests.
setUpAll(() async {
driver = await FlutterDriver.connect();
});
test('check flutter driver health', () async {
Health health = await driver.checkHealth();
print(health.status);
});
test("Check login screen", () async {
await driver.waitFor(find.byType("AuthScreen")).then((value) async {
print("Auth screen");
});
});
// Close the connection to the driver after the tests have completed.
tearDownAll(() async {
if (driver != null) {
driver.close();
}
});
});
}
Piece of futureBuilder code in the main class:
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return SplashScreen(key: Key("splashScreen2"));
} else if (snapshot.hasData) {
return DashboardScreen();
} else {
return AuthScreen();
}
},
and the AuthScreen() piece of code:
class AuthScreen extends StatelessWidget {
static const routeName = '/auth';
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
return Scaffold(
key: Key("auth_screen"),
backgroundColor: Colors.white,
test() has a param called timeout
Here's demo:
test("Check login screen", () async {
await driver.waitFor(find.byType("AuthScreen")).then((value) async {
print("Auth screen");
});
}, timeout:Timeout.none);
which timeout defaults value = 30 seconds;