Flutter show local notifications at exact time using workmanager

1.2k views Asked by At

I want to set the local notification in flutter (android app), I got to know about workmanager package but I don't know how to use it for my purpose. I want to send notifications at the exact time which is selected by the user.

Time Selection:

Container(
          padding: EdgeInsets.all(15),
          child: IconButton(icon: Icon(Icons.alarm, color: Colors.white, size: 70,),
              onPressed: (){
                DatePicker.showTimePicker(context,
                    theme: DatePickerTheme(
                      containerHeight: 210.0,
                    ),
                    showTitleActions: true, onConfirm: (time) {
                      ttime = '${time.hour} : ${time.minute} : ${time.second}';
                      //print(_time);
                      _setNotifyTime();
                      setState(() {});
                    }, currentTime: DateTime.now(), locale: LocaleType.en);
                setState(() {});
              }),
        ),

Saving this time in sharedpreferences:

Future<void> _setNotifyTime() async{
final prefs = await SharedPreferences.getInstance();
final savedNotifyTime = await _getStringFromSharedPrefs();
await prefs.setString('notificationTime', ttime);
//print("this $savedNotifyTime");
return savedNotifyTime;
}

Getting this time from shared preferences:

Future<String> _getStringFromSharedPrefs() async{
final prefs = await SharedPreferences.getInstance();
notifyTime = prefs.getString('notificationTime');
return notifyTime;
}

Flutter local notification as per selected time:

Future<void> showDailyAtTime() async {
var splited = notifyTime.split(':');
int hour = int.parse(splited[0]);
int minute = int.parse(splited[1]);
int second = int.parse(splited[2]);
print(hour.toString());
print(minute.toString());
print(second.toString());
var reviewTime = Time(hour, minute, second);
var androidChannelSpecifics = AndroidNotificationDetails(
  'CHANNEL_ID 2',
  'CHANNEL_NAME 2',
  "CHANNEL_DESCRIPTION 2",
  importance: Importance.max,
  priority: Priority.high,
);
var iosChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics =
NotificationDetails(android: androidChannelSpecifics, iOS: iosChannelSpecifics);
await flutterLocalNotificationsPlugin.showDailyAtTime(
  0,
  'Title at ${reviewTime.hour}:${reviewTime.minute}.${reviewTime.second}',
  'Test Body', //null
  reviewTime,
  platformChannelSpecifics,
  payload: 'Test Payload',
 );
}

Workmanager implementation:

void callbackDispatcher() {
Workmanager().executeTask((taskName, inputData) async {
notificationPlugin.showDailyAtTime();
return Future.value(true);
 });
}

void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Workmanager().initialize(callbackDispatcher);
 await Workmanager().registerPeriodicTask("test_workertask", "test_workertask",
 inputData: {"data1": "value1", "data2": "value2"},
 frequency: Duration(minutes: 15),
 initialDelay: Duration(minutes: 15));
 runApp(MyApp());
} 

This is working when I am calling it from a button in-app but as soon as I go in the background it doesn't work. It is giving the following error, when I implement the above code:

HostComposition ext ANDROID_EMU_CHECKSUM_HELPER_v1 ANDROID_EMU_dma_v1   ANDROID_EMU_direct_mem ANDROID_EMU_host_composition_v1  ANDROID_EMU_host_composition_v2 ANDROID_EMU_vulkan  ANDROID_EMU_deferred_vulkan_commands ANDROID_EMU_vulkan_null_optional_strings  ANDROID_EMU_vulkan_create_resources_with_requirements ANDROID_EMU_YUV_Cache  ANDROID_EMU_async_unmap_buffer ANDROID_EMU_vulkan_ignored_handles  ANDROID_EMU_vulkan_free_memory_sync ANDROID_EMU_vulkan_shader_float16_int8  ANDROID_EMU_vulkan_async_queue_submit GL_OES_vertex_array_object  GL_KHR_texture_compression_astc_ldr ANDROID_EMU_host_side_tracing  ANDROID_EMU_async_frame_commands ANDROID_EMU_gles_max_version_2 
D/EGL_emulation( 6089): eglMakeCurrent: 0xe90561a0: ver 2 0 (tinfo  0xe0eadb70)
E/flutter ( 6089): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled   Exception: NoSuchMethodError: The method 'split' was called on null. 
E/flutter ( 6089): Receiver: null
E/flutter ( 6089): Tried calling: split(":")
E/flutter ( 6089): #0      Object.noSuchMethod (dart:core- patch/object_patch.dart:54:5)
E/flutter ( 6089): #1      NotificationPlugin.showDailyAtTime (package:finance_manager/widgets/NotificationPlugin.dart:107:30)
E/flutter ( 6089): #2      callbackDispatcher.<anonymous closure> (package:finance_manager/main.dart:14:24)
E/flutter ( 6089): #3      callbackDispatcher.<anonymous closure> (package:finance_manager/main.dart:12:29)
E/flutter ( 6089): #4      Workmanager.executeTask.<anonymous closure> (package:workmanager/src/workmanager.dart:89:28)
E/flutter ( 6089): #5      Workmanager.executeTask.<anonymous closure> (package:workmanager/src/workmanager.dart:87:45)
E/flutter ( 6089): #6      MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:435:55)
E/flutter ( 6089): #7      MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:382:34)
E/flutter ( 6089): #8      _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binding.dart:284:33)
E/flutter ( 6089): #9      _invoke3.<anonymous closure> (dart:ui/hooks.dart:221:15)
E/flutter ( 6089): #10     _rootRun (dart:async/zone.dart:1354:13)
E/flutter ( 6089): #11     _CustomZone.run (dart:async/zone.dart:1258:19)
E/flutter ( 6089): #12     _CustomZone.runGuarded (dart:async/zone.dart:1162:7)
E/flutter ( 6089): #13     _invoke3 (dart:ui/hooks.dart:220:10)
E/flutter ( 6089): #14     PlatformDispatcher._dispatchPlatformMessage (dart:ui/platform_dispatcher.dart:457:7)
E/flutter ( 6089): #15     _dispatchPlatformMessage (dart:ui/hooks.dart:90:31)
E/flutter ( 6089): 
I/WM-WorkerWrapper( 6089): Worker result SUCCESS for Work [ id=87272c98-d905-4b69-a1ce-86c13336ab8b, tags={ be.tramckrijte.workmanager.BackgroundWorker }  ]

Please help me.

Thanks for your replies in advance.

1

There are 1 answers

0
Olumide On

The problem is that callbackDispatcher runs in an isolate. So it doesn't have access to any variables that is not defined inside it. You should pass in the time with the inputData variable. You will also need to convert the DateTime into a String since the inputData only supports basic data types. showDailyAtTime also probably has to be static for it to be accessible to callbackDispatcher.

The end result should be something like this:

static Future<void> showDailyAtTime(DateTime reviewTime) async {
  ...
}

void callbackDispatcher() {
  Workmanager().executeTask((taskName, inputData) async {
    final time = DateTime.parse(inputData!['time']);

    notificationPlugin.showDailyAtTime(time);
    return Future.value(true);
 });
}

void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Workmanager().initialize(callbackDispatcher);
 await Workmanager().registerPeriodicTask("test_workertask", "test_workertask",
 inputData: {"time": notifyTime.toIso8601String()},
 frequency: Duration(minutes: 15),
 initialDelay: Duration(minutes: 15));
 runApp(MyApp());
}