I am currently working on a Habit Tracker app in Flutter. I want to send a notification to the user at 11PM everyday mentioning the number of Habits they have left for the day, if there is any
My Problem: The callback function runs every time I hot restart the app, or in cases where the device is not connected, it runs every time I close the app and open it again. This happens until the end of the day (12:00 AM). What is the fix to this? How do I make it send the notification only once a day. This feels like a simple fix but I'm not sure what I'm doing wrong.
My Code:
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
void scheduleNotification() async
{
print("scheduleNotification() function Ran");
var scheduledNotificationDateTime = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 23, 00, 00);
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'habit_notif',
'habit_notif',
channelDescription: 'Channel for Habit notification',
icon: 'applogocircle2',
largeIcon: DrawableResourceAndroidBitmap('applogoyellowsquare'),
);
var iOSPlatformChannelSpecifics = IOSNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true
);
var platformChannelSpecifics = NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics
);
List<Map<String, dynamic>> queryRows = await DatabaseHelper.instance.queryAllValues();
int completed = queryRows[0]["todayCompleted"];
int total = queryRows[0]["todayTotal"];
if(completed != total)
{
await flutterLocalNotificationsPlugin.schedule(
0,
'You haven\'t completed all your habits for today!',
"You have ${total-completed} habits left",
scheduledNotificationDateTime,
platformChannelSpecifics,
androidAllowWhileIdle: true,
);
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await UserSimplePreferences.init();
runApp(const MyApp());
WidgetsFlutterBinding.ensureInitialized();
var initializationSettingsAndroid = AndroidInitializationSettings('applogo');
var initializationSettingsIOS = IOSInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
onDidReceiveLocalNotification: (int id, String? title, String? body, String? payload) async {}
);
var initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS
);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onSelectNotification: (String? payload) async {
if (payload != null) {
debugPrint('Notification payload: ' + payload);
}
}
);
scheduleNotification();
);
}
@Zuhaib First of all, my answer is about to be theoretical because I don't see your project, but I hope you understand, I am sharing a simple yet effective technique while working with notifying users, forgetting username/password, etc.
Let me explain what I have done while working in a situation like this, first of all in the table (you might use a database to store user data), here I will add a column for a user table let's call it "isNotified" int data type and "notifiedAt" which is a DateTime data type, nextNotifiedAt which is a DateTime data type as well, this one stores the next 24H value, something like this:
before:
isNotified = 0
notifiedAt = null
nextNotifiedAt = null
after notified:
isNotified = 1
notifiedAt = 2/12/2022 11:00PM
nextNotifiedAt = 3/12/2022 11:00PM
Now, when users need to be notified, you will get the user's info from the database, username and how many tasks he/she needs to do... here at:
List<Map<String, dynamic>> queryRows = await DatabaseHelper.instance.queryAllValues();
int completed = queryRows[0]["todayCompleted"];
int total = queryRows[0]["todayTotal"]
you have to retrieve those 3 columns you also get "isNotified" and "notifiedAt" for user "Awat" => isNotified = 0 and notifiedAt = NULL meaning I am not notified yet, when you send me a notification for the first time at:
if(completed != total){
.....
// IS it my first time you notifiy me?
if (isNotified == 0){
DO UPDATE isNotified = 1 and notifiedAt = CURRENT DATETIME (in mysql use getdate());
// update tbl_user set isNotified = 1, notifiedAt = getdate() where user_id = X
}else{
// at anytime you have notified me, this is a new day..
if (notifiedAt > nextNotifiedAt){
// DO UPDATE
}
}
}
by doing this you send only 1 time, also you can store when you have notified the user to do the task...
don't use local datetime here at:
var scheduledNotificationDateTime = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 23, 00, 00);
as you know users can change their DateTime manually, so get DateTime from the server (your database server), and then make the difference between current users DateTime and your database server's DateTime
var dbDateTime = getDateTimeFromServer()
var localDateTime = DateTime(...)
if the user changed the date and time from his/her cellphone you have both values stored in a database no one can access those, notifiedAt = 2/12/2022 11:00 PM nextNotifiedAt = 3/12/2022 11:00 PM
hope you refactor your code and implement this, also I do apologize most of it is theory.