Created
December 20, 2019 16:32
-
-
Save ferrarafer/3aa16bd5f765724003eb45e739c5947c to your computer and use it in GitHub Desktop.
Local Notifications
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LocalNotificationManager extends StatefulWidget { | |
final Widget child; | |
LocalNotificationManager({Key key, this.child}) : super(key: key); | |
_LocalNotificationManagerState createState() => _LocalNotificationManagerState(); | |
} | |
class _LocalNotificationManagerState extends State<LocalNotificationManager> { | |
final _dialogService = locator<DialogService>(); | |
final _eventsService = locator<EventsService>(); | |
final _leadsService = locator<LeadsService>(); | |
final _navigationService = locator<NavigationService>(); | |
final _notificationPlugin = FlutterLocalNotificationsPlugin(); | |
final _notificationService = locator<LocalNotificationService>(); | |
@override | |
void initState() { | |
super.initState(); | |
_initLocalNotificationPlugin(); | |
_notificationService.registerTaskListener(_scheduleNotification); | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return widget.child; | |
} | |
/// Handle task registered by the local notification service | |
Future<void> _scheduleNotification(LocalNotificationRequest request) async { | |
if (request.needCancellation) { | |
await _cancelNotification(request.event); | |
return; | |
} | |
switch (request.type) { | |
case NotificationType.SingleInstant: | |
await _show(request.event); | |
break; | |
case NotificationType.SingleScheduled: | |
await _schedule(request.event); | |
break; | |
case NotificationType.RecurrentDaily: | |
break; | |
case NotificationType.RecurrentPeriodically: | |
break; | |
case NotificationType.RecurrentWeekly: | |
break; | |
} | |
} | |
/// Initialize the flutter local notification plugin | |
void _initLocalNotificationPlugin() { | |
var initializationSettingsAndroid = AndroidInitializationSettings('notification_icon'); | |
var initializationSettingsIOS = IOSInitializationSettings( | |
onDidReceiveLocalNotification: _onDidReceiveLocalNotification | |
); | |
var initializationSettings = InitializationSettings( | |
initializationSettingsAndroid, initializationSettingsIOS | |
); | |
_notificationPlugin.initialize( | |
initializationSettings, | |
onSelectNotification: _onSelectNotification | |
); | |
} | |
/// Get notification settings for each platform | |
NotificationDetails _getPlatformChannelSpecifics() { | |
var androidPlatformChannelSpecifics = AndroidNotificationDetails( | |
'App Scheduled Events', | |
'Scheduled Events', | |
'App Scheduled Events (Calls & Meetings)', | |
importance: Importance.Max, | |
priority: Priority.High, | |
ticker: 'App Ticker' | |
); | |
var iOSPlatformChannelSpecifics = IOSNotificationDetails(); | |
return NotificationDetails( | |
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics | |
); | |
} | |
Future<void> _show(AbstractEvent event) async { | |
var notificationId = 99999; | |
var platformChannelSpecifics = _getPlatformChannelSpecifics(); | |
var payload = json.encode({ | |
'event_id': event.id, | |
'lead_id': event.lead?.id, | |
}); | |
await _notificationPlugin.show( | |
notificationId, | |
event.name, | |
event.description, | |
platformChannelSpecifics, | |
payload: payload | |
); | |
_notificationService.taskComplete(LocalNotificationResponse( | |
eventId: event.id, | |
leadId: event.lead?.id, | |
notificationId: notificationId, | |
)); | |
} | |
Future<void> _schedule(AbstractEvent event) async { | |
var payload; | |
var pendingRequests = await _notificationPlugin.pendingNotificationRequests(); | |
for (var request in pendingRequests) { | |
payload = json.decode(request.payload); | |
if (payload['event_id'] == event.id) { | |
_notificationService.taskComplete(LocalNotificationResponse( | |
eventId: event.id, | |
leadId: event.lead?.id, | |
notificationId: request.id | |
)); | |
return; | |
} | |
} | |
var platformChannelSpecifics = _getPlatformChannelSpecifics(); | |
var idsInUse = _getIdsInUse(pendingRequests); | |
var notificationId = _getAvailableId(idsInUse); | |
payload = json.encode({ | |
'event_id': event.id, | |
'lead_id': event.lead?.id, | |
}); | |
await _notificationPlugin.schedule( | |
notificationId, | |
event.name, | |
event.description, | |
event.dateStart, | |
platformChannelSpecifics, | |
payload: payload | |
); | |
_notificationService.taskComplete(LocalNotificationResponse( | |
eventId: event.id, | |
leadId: event.lead?.id, | |
notificationId: notificationId, | |
isScheduled: true | |
)); | |
} | |
/// Removes the pending notification from the system. | |
Future<void> _cancelNotification(AbstractEvent event) async { | |
var payload; | |
var pendingRequests = await _notificationPlugin.pendingNotificationRequests(); | |
for (var request in pendingRequests) { | |
payload = json.decode(request.payload); | |
if (payload['event_id'] == event.id) { | |
await _notificationPlugin.cancel(request.id); | |
_notificationService.taskComplete(LocalNotificationResponse( | |
eventId: event.id, | |
leadId: event.lead?.id, | |
notificationId: request.id, | |
isCancelled: true | |
)); | |
return; | |
} | |
} | |
_notificationService.taskComplete(LocalNotificationResponse( | |
eventId: event.id, | |
leadId: event.lead?.id | |
)); | |
} | |
/// Should fire when a notification has been tapped on | |
/// | |
/// If there is a payload, the event is remove from pending | |
/// future events, and the badger counters decrease. | |
/// If there is no payload, a dialog is shown. | |
Future _onSelectNotification(String payload) async { | |
if (payload == null) { | |
await _dialogService.showDialog( | |
'Event', | |
'Event information is not available.' | |
); | |
return; | |
} | |
var payloadMap = json.decode(payload); | |
Lead lead = _leadsService.leads | |
.singleWhere((l) => l.id == payloadMap['lead_id']); | |
AbstractEvent event = lead.events | |
.singleWhere((e) => e.id == payloadMap['event_id']); | |
_eventsService.removePendingFutureEvent(event); | |
var args = EditLeadViewArguments(event: event, lead: lead); | |
_navigationService.navigateTo(routes.EditLeadRoute, arguments: args); | |
} | |
/// Support for iOS older versions than iOS10 | |
/// display a dialog with the notification details, tap ok to go to another page | |
Future<void> _onDidReceiveLocalNotification( | |
int id, | |
String title, | |
String body, | |
String payload | |
) async { | |
print('onDidReceiveLocalNotification'); | |
AlertResponse dialogResponse = await _dialogService.showDialog( | |
title, | |
body, | |
buttonAccept: 'Open' | |
); | |
if (dialogResponse.confirmed == false) return; | |
if (payload == null) { | |
await _dialogService.showDialog( | |
'Event', | |
'Event information is not available.' | |
); | |
return; | |
} | |
var payloadMap = json.decode(payload); | |
Lead lead = _leadsService.leads | |
.singleWhere((l) => l.id == payloadMap['lead_id']); | |
AbstractEvent event = lead.events | |
.singleWhere((e) => e.id == payloadMap['event_id']); | |
_eventsService.removePendingFutureEvent(event); | |
var args = EditLeadViewArguments(event: event, lead: lead); | |
_navigationService.navigateTo(routes.EditLeadRoute, arguments: args); | |
} | |
/// Get the list of the IDs used for the pending | |
/// notifications waiting for their time to appear | |
List<int> _getIdsInUse(List requests) { | |
List<int> inUse = []; | |
for (var request in requests) { | |
inUse.add(request.id); | |
} | |
return inUse; | |
} | |
/// Get the first available ID for local notification | |
int _getAvailableId(List<int> idsInUse) { | |
bool stayInLoop = true; | |
int availableId = 0; | |
while(stayInLoop) { | |
availableId++; | |
if (!idsInUse.contains(availableId)) { | |
stayInLoop = false; | |
} | |
} | |
return availableId; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LocalNotificationRequest { | |
final AbstractEvent event; | |
final bool needCancellation; | |
final NotificationType type; | |
LocalNotificationRequest({ | |
this.event, | |
this.needCancellation = false, | |
this.type | |
}); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LocalNotificationResponse { | |
final String eventId; | |
final String leadId; | |
final int notificationId; | |
final bool isCancelled; | |
final bool isScheduled; | |
LocalNotificationResponse({ | |
this.eventId, | |
this.leadId, | |
this.notificationId, | |
this.isCancelled = false, | |
this.isScheduled = false | |
}); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LocalNotificationService { | |
Function _registerTaskListener; | |
Completer _taskCompleter; | |
/// Registers a callback function. Typically to register a task. | |
void registerTaskListener(Function(LocalNotificationRequest) registerTaskListener) { | |
_registerTaskListener = registerTaskListener; | |
} | |
/// Calls the task listener and returns a Future that will wait for taskComplete. | |
Future<LocalNotificationResponse> scheduleNotification( | |
AbstractEvent event, { | |
bool needCancellation = false, | |
NotificationType type = NotificationType.SingleScheduled | |
}) { | |
_taskCompleter = Completer<LocalNotificationResponse>(); | |
_registerTaskListener(LocalNotificationRequest( | |
event: event, | |
needCancellation: needCancellation, | |
type: type | |
)); | |
return _taskCompleter.future; | |
} | |
/// Completes the _taskCompleter to resume the Future's execution call | |
void taskComplete(LocalNotificationResponse response) { | |
_taskCompleter.complete(response); | |
_taskCompleter = null; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment