Skip to content

Instantly share code, notes, and snippets.

@ferrarafer
Created December 20, 2019 16:32
Show Gist options
  • Save ferrarafer/3aa16bd5f765724003eb45e739c5947c to your computer and use it in GitHub Desktop.
Save ferrarafer/3aa16bd5f765724003eb45e739c5947c to your computer and use it in GitHub Desktop.
Local Notifications
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;
}
}
class LocalNotificationRequest {
final AbstractEvent event;
final bool needCancellation;
final NotificationType type;
LocalNotificationRequest({
this.event,
this.needCancellation = false,
this.type
});
}
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
});
}
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