Reputation: 3298
I have an API that I'm checking, and when the response changes I need to send a notification to the user. I would like to know how to do this without FCM Push Notifications.
I'm using flutter-local-notifications
and background fetch (https://github.com/transistorsoft/flutter_background_fetch) to do it. On the background fetch docs it says that background fetch will do your function once every 15 minutes, which is good enough for me.
This is my initPlatformState()
:
Future<void> initPlatformState() async {
// Load persisted fetch events from SharedPreferences
SharedPreferences prefs = await SharedPreferences.getInstance();
String json = prefs.getString(EVENTS_KEY);
if (json != null) {
setState(() {
_events = jsonDecode(json).cast<String>();
});
}
// Configure BackgroundFetch.
BackgroundFetch.configure(
BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
forceReload: true,
startOnBoot: true,
),
_onBackgroundFetch)
.then((int status) {
print('[BackgroundFetch] SUCCESS: $status');
setState(() {
_status = status;
});
}).catchError((e) {
print('[BackgroundFetch] ERROR: $e');
setState(() {
_status = e;
});
});
// Optionally query the current BackgroundFetch status.
int status = await BackgroundFetch.status;
setState(() {
_status = 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'm assuming that what's in the function that gets called in the fetch isn't needed for the question.
I tried this on my phone and simulator, and I used Xcode -> Simulate Background Fetch and it ran properly. It also ran properly when I opened the app. Unfortunately, it didn't run after 15 minutes. Is there something I'm missing?
How would I change my code to make the background fetch to happen every 15 minutes?
Upvotes: 2
Views: 6085
Reputation: 6127
As the answer of @Habnarim suggested I have used workmanager with flutter_local_notifications to implement local notifications in the background.
Generally, I followed this article, so it can be referred to for more details, but the code there is a bit outdated. I have fixed some discrepancies and also put everything related to notifications in a separate library.
Add workmanager, flutter_local_notifications and cupertino_icons to project.
Add the below permission inside the 'manifest' tag
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Add the below permission inside the 'application' tag
<receiver android:name="com.dexterous.flutterlocalnotifications.
ScheduledNotificationBootReceiver">
<intent-filter>
<action
android:name="android.intent.action.BOOT_COMPLETED"/>
<action
android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
</intent-filter>
</receiver>
Add code:
notifications.dart
import 'dart:io';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:workmanager/workmanager.dart';
// Workmanager input data:
const _uniqueName = 'whatevername_1';
const _taskName = 'whatevername_2';
Duration? _frequency; // = Duration(minutes: 15); // cannot be less
_calculateFrequency() {
// some logic here, db connection etc.
return Duration(minutes: 15);
}
// Notifications plugin input data
const _channelId = '1234';
const _channelName = 'whatever_name';
const _channelDescription = 'whatever_description';
const _notificationTitle = 'probably just App name';
String _message = 'default message'; //if message is always the same make const
_createMessage() {
// some logic here, db connection etc.
return 'Hello from my app!';
}
void initLocalNotifications() async {
if (Platform.isWindows) {
return;
}
_frequency = await _calculateFrequency();
Workmanager().initialize(callbackDispatcher,
isInDebugMode: true); //TODO change debug mode
Workmanager().registerPeriodicTask(
_uniqueName,
_taskName,
frequency: _frequency,
);
}
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) {
FlutterLocalNotificationsPlugin notifPlugin =
new FlutterLocalNotificationsPlugin();
var android = new AndroidInitializationSettings('@mipmap/ic_launcher');
var ios = new DarwinInitializationSettings();
var settings = new InitializationSettings(iOS: ios, android: android);
notifPlugin.initialize(settings);
_showNotificationWithDefaultSound(notifPlugin);
return Future.value(true);
});
}
Future _showNotificationWithDefaultSound(notifPlugin) async {
_message = await _createMessage();
var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
_channelId, _channelName,
channelDescription: _channelDescription,
importance: Importance.max,
priority: Priority.high);
var iOSPlatformChannelSpecifics = new DarwinNotificationDetails();
var platformChannelSpecifics = new NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics);
await notifPlugin.show(
0, _notificationTitle, _message, platformChannelSpecifics,
payload: 'Default_Sound');
}
main.dart
void main() async {
initLocalNotifications();
runApp(
const ProviderScope(child: MainApp()),
);
}
I have tested the above on an Android device and it kinda works. I.e. when the app is in the background notifications appear.
Two problems. First, I don't want to show notifications when the app is in the foreground. It can be done but the solution seems to be too complex. Second, notifications do not appear when the app is forced to quit. My guess is that users of low-end devices remove apps from memory frequently. So, currently, I think, that I will use push notifications instead.
Upvotes: 0
Reputation: 29
Yes, I spent lot of time trying to solve this but was not successful so I switched, I guessed what you are trying to do is to handle your own notification without Firebase or from an API like me, well this is it, after my search I was able to do this with the help of work manager package. So easy to use and implement, try it.
https://pub.dev/packages/workmanager
Upvotes: 3