Membangun Notifikasi Lokal di Flutter dengan Awesome Notifications

0
117
Baraja Coding

Notifikasi lokal adalah fitur yang penting dalam pengembangan aplikasi mobile. Mereka memungkinkan Anda untuk berkomunikasi dengan pengguna dan memberikan informasi penting, bahkan ketika aplikasi tidak aktif. Di artikel ini, kita akan menjelaskan bagaimana Anda dapat mengintegrasikan notifikasi lokal ke dalam aplikasi Flutter Anda menggunakan paket “awesome_notifications.”

Apa itu Awesome Notifications?

Awesome Notifications adalah paket Flutter yang kuat untuk mengelola notifikasi lokal. Paket ini menyediakan antarmuka yang mudah digunakan untuk membuat, menjadwalkan, dan menyesuaikan notifikasi sesuai kebutuhan Anda. Fitur-fitur utama Awesome Notifications meliputi:

  1. Inisialisasi Notifikasi: Anda dapat dengan mudah menginisialisasi notifikasi lokal dalam aplikasi Anda dengan mengkonfigurasi kanal notifikasi dan pengaturan umum lainnya.
  2. Membuat Notifikasi: Anda dapat membuat notifikasi dengan berbagai tipe konten, termasuk teks, gambar, tindakan kustom, dan lainnya.
  3. Menjadwalkan Notifikasi: Paket ini memungkinkan Anda menjadwalkan notifikasi agar muncul pada waktu yang ditentukan.
  4. Mengelola Aksi Notifikasi: Anda dapat menangani tindakan yang diambil pengguna saat mereka berinteraksi dengan notifikasi, seperti mengarahkan mereka ke layar tertentu atau menjalankan tindakan tertentu.
  5. Mengelola Izin: Awesome Notifications membantu Anda meminta izin pengguna untuk menampilkan notifikasi di perangkat mereka.
  6. Menyediakan Statistik: Anda dapat melacak dan mengelola statistik notifikasi, seperti jumlah notifikasi yang ditampilkan dan dihapus oleh pengguna.

Berikut Contoh Projectnya :

Output :

ketika me reply message

hasilnya di log

Source Code :

main.dart

import 'package:flutter/material.dart';
import 'package:artikel/localnotification/notificationcontroller.dart';
import 'package:artikel/localnotification/page.dart';

void main() async {
  await NotificationController.initializeLocalNotifications();
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({super.key});
  static final GlobalKey<NavigatorState> navigatorKey =
      GlobalKey<NavigatorState>();
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        navigatorKey: navigatorKey,
        debugShowCheckedModeBanner: false,
        home: NotifyPage());
  }
}

page.dart

import 'package:artikel/localnotification/notificationcontroller.dart';
import 'package:flutter/material.dart';

class NotifyPage extends StatefulWidget {
  const NotifyPage({Key? key}) : super(key: key);

  @override
  _NotifyPageState createState() => _NotifyPageState();
}

class _NotifyPageState extends State<NotifyPage> {
  @override
  void initState() {
    super.initState();
    NotificationController.startListeningNotificationEvents();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            MaterialButton(
              color: Colors.lightBlue,
              onPressed: () {
                NotificationController.createNewNotification();
              },
              child: Text('Show Notification'),
            ),
            MaterialButton(
              color: Colors.lightBlue,
              onPressed: () {
                NotificationController.scheduleNewNotification();
              },
              child: Text('Show Notification Schedule'),
            ),
          ],
        ),
      ),
    );
  }
}

notificationcontroller.dart

import 'dart:isolate';
import 'dart:ui';

import 'package:artikel/main.dart';
import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:flutter/material.dart';

class NotificationController {
  static ReceivedAction? initialAction;

  ///  *********************************************
  ///     INITIALIZATIONS
  ///  *********************************************
  ///
  static Future<void> initializeLocalNotifications() async {
    await AwesomeNotifications().initialize(
        null,
        [
          NotificationChannel(
            channelKey:
                'basic_channel', // Tambahkan kanal notifikasi 'basic_channel'
            channelName: 'Basic Channel',
            channelDescription: 'Notification channel for basic notifications',
            playSound: true,
            defaultColor: Colors.deepPurple,
            ledColor: Colors.deepPurple,
          ),
          NotificationChannel(
            channelKey: 'alerts',
            channelName: 'Alerts',
            channelDescription: 'Notification tests as alerts',
            playSound: true,
            onlyAlertOnce: true,
            groupAlertBehavior: GroupAlertBehavior.Children,
            importance: NotificationImportance.High,
            defaultPrivacy: NotificationPrivacy.Private,
            defaultColor: Colors.deepPurple,
            ledColor: Colors.deepPurple,
          ),
        ],
        debug: true);

    // Get initial notification action is optional
    initialAction = await AwesomeNotifications()
        .getInitialNotificationAction(removeFromActionEvents: false);
  }

  static ReceivePort? receivePort;
  static Future<void> initializeIsolateReceivePort() async {
    receivePort = ReceivePort('Notification action port in main isolate')
      ..listen(
          (silentData) => onActionReceivedImplementationMethod(silentData));

    // This initialization only happens on main isolate
    IsolateNameServer.registerPortWithName(
        receivePort!.sendPort, 'notification_action_port');
  }

  ///  *********************************************
  ///     NOTIFICATION EVENTS LISTENER
  ///  *********************************************
  ///  Notifications events are only delivered after call this method
  static Future<void> startListeningNotificationEvents() async {
    AwesomeNotifications().setListeners(
        onActionReceivedMethod: onActionReceivedMethod,
        onNotificationDisplayedMethod: onNotificationDisplayedMethod,
        onDismissActionReceivedMethod: onDismiss);
  }

  ///  *********************************************
  ///     NOTIFICATION EVENTS
  ///  *********************************************
  ///
  @pragma('vm:entry-point')
  static Future<void> onActionReceivedMethod(
      ReceivedAction receivedAction) async {
    if (receivedAction.actionType == ActionType.SilentAction ||
        receivedAction.actionType == ActionType.SilentBackgroundAction) {
      // For background actions, you must hold the execution until the end
      print(
          'Message sent via notification input: "${receivedAction.buttonKeyInput}"');
      // await executeLongTaskInBackground();
    } else {
      // this process is only necessary when you need to redirect the user
      // to a new page or use a valid context, since parallel isolates do not
      // have valid context, so you need redirect the execution to main isolate
      if (receivePort == null) {
        print(
            'onActionReceivedMethod was called inside a parallel dart isolate.');
        SendPort? sendPort =
            IsolateNameServer.lookupPortByName('notification_action_port');

        if (sendPort != null) {
          print('Redirecting the execution to main isolate process.');
          sendPort.send(receivedAction);
          return;
        }
      }

      return onActionReceivedImplementationMethod(receivedAction);
    }
  }

  @pragma('vm:entry-point')
  static Future<void> onNotificationDisplayedMethod(
      ReceivedNotification receivedNotification) async {
    print("AKAN DI PANGGIL JIKA NOTIFIKASINYA MUNCUL");
    AwesomeNotifications().incrementGlobalBadgeCounter();
  }

  @pragma('vm:entry-point')
  static Future<void> onDismiss(
      ReceivedNotification receivedNotification) async {
    print("AKAN DI PANGGIL JIKA NOTIFIKASINYA DISMISS");
    AwesomeNotifications().decrementGlobalBadgeCounter();
  }

  static Future<void> onActionReceivedImplementationMethod(
      ReceivedAction receivedAction) async {
    // MyApp.navigatorKey.currentState?.pushNamedAndRemoveUntil(
    //     '/notification-page',
    //     (route) =>
    //         (route.settings.name != '/notification-page') || route.isFirst,
    //     arguments: receivedAction);
  }

  ///  *********************************************
  ///     REQUESTING NOTIFICATION PERMISSIONS
  ///  *********************************************
  ///
  static Future<bool> displayNotificationRationale() async {
    bool userAuthorized = false;
    BuildContext context = MyApp.navigatorKey.currentContext!;
    await showDialog(
        context: context,
        builder: (BuildContext ctx) {
          return AlertDialog(
            title: Text('Get Notified!',
                style: Theme.of(context).textTheme.titleLarge),
            content: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Row(
                  children: [
                    Expanded(
                      child: Image.asset(
                        'assets/images/animated-bell.gif',
                        height: MediaQuery.of(context).size.height * 0.3,
                        fit: BoxFit.fitWidth,
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 20),
                const Text(
                    'Allow Awesome Notifications to send you beautiful notifications!'),
              ],
            ),
            actions: [
              TextButton(
                  onPressed: () {
                    Navigator.of(ctx).pop();
                  },
                  child: Text(
                    'Deny',
                    style: Theme.of(context)
                        .textTheme
                        .titleLarge
                        ?.copyWith(color: Colors.red),
                  )),
              TextButton(
                  onPressed: () async {
                    userAuthorized = true;
                    Navigator.of(ctx).pop();
                  },
                  child: Text(
                    'Allow',
                    style: Theme.of(context)
                        .textTheme
                        .titleLarge
                        ?.copyWith(color: Colors.deepPurple),
                  )),
            ],
          );
        });
    return userAuthorized &&
        await AwesomeNotifications().requestPermissionToSendNotifications();
  }

  ///  *********************************************
  ///     BACKGROUND TASKS TEST
  ///  *********************************************
  static Future<void> executeLongTaskInBackground() async {
    print("starting long task");
    // await Future.delayed(const Duration(seconds: 4));
    // final url = Uri.parse("http://google.com");
    // final re = await http.get(url);
    // print(re.body);
    print("long task done");
  }

  ///  *********************************************
  ///     NOTIFICATION CREATION METHODS
  ///  *********************************************
  ///
  static Future<void> createNewNotification() async {
    bool isAllowed = await AwesomeNotifications().isNotificationAllowed();
    if (!isAllowed) isAllowed = await displayNotificationRationale();
    if (!isAllowed) return;

    await AwesomeNotifications().createNotification(
        content: NotificationContent(
            id: DateTime.now()
                .millisecondsSinceEpoch
                .remainder(100000), // -1 is replaced by a random number
            channelKey: 'alerts',
            title: 'Hai Ini Notifikasi',
            body:
                "Selamat datang di artikel Notifikasi Lokal By Muhamad Fadhel",
            bigPicture:
                'https://storage.googleapis.com/cms-storage-bucket/d406c736e7c4c57f5f61.png',
            largeIcon: 'https://storage.googleapis.com/cms-storage-bucket/0dbfcc7a59cd1cf16282.png',
            //'asset://assets/images/balloons-in-sky.jpg',
            notificationLayout: NotificationLayout.BigPicture,
            payload: {'notificationId': '1234567890'}),
        actionButtons: [
          NotificationActionButton(key: 'REDIRECT', label: 'Redirect'),
          NotificationActionButton(
              key: 'REPLY',
              label: 'Reply Message',
              requireInputText: true,
              actionType: ActionType.SilentAction),
          NotificationActionButton(
              key: 'DISMISS',
              label: 'Dismiss',
              actionType: ActionType.DismissAction,
              isDangerousOption: true)
        ]);
  }

  static Future<void> scheduleNewNotification() async {
    bool isAllowed = await AwesomeNotifications().isNotificationAllowed();
    if (!isAllowed) isAllowed = await displayNotificationRationale();
    if (!isAllowed) return;

    await myNotifyScheduleInHours(
        title: 'test',
        msg: 'test message',
        heroThumbUrl:
            'https://storage.googleapis.com/cms-storage-bucket/d406c736e7c4c57f5f61.png',
        hoursFromNow: 5,
        username: 'test user',
        repeatNotif: false);
  }

  static Future<void> resetBadgeCounter() async {
    await AwesomeNotifications().resetGlobalBadge();
  }

  static Future<void> cancelNotifications() async {
    await AwesomeNotifications().cancelAll();
  }
}

Future<void> myNotifyScheduleInHours({
  required int hoursFromNow,
  required String heroThumbUrl,
  required String username,
  required String title,
  required String msg,
  bool repeatNotif = false,
}) async {
  await Future.delayed(const Duration(seconds: 5));

  await AwesomeNotifications().createNotification(
    content: NotificationContent(
      id: DateTime.now().millisecondsSinceEpoch.remainder(100000),
      channelKey: 'basic_channel',
      title: '${Emojis.food_bowl_with_spoon} $title',
      body: '$username, $msg',
      bigPicture: heroThumbUrl,
      notificationLayout: NotificationLayout.BigPicture,
      color: Colors.black,
      backgroundColor: Colors.black,
      payload: {'actPag': 'myAct', 'actType': 'food', 'username': username},
    ),
    actionButtons: [
      NotificationActionButton(
        key: 'NOW',
        label: 'btnAct1',
      ),
      NotificationActionButton(
        key: 'LATER',
        label: 'btnAct2',
      ),
    ],
  );
}

Kesimpulan

Notifikasi lokal adalah fitur penting dalam mengembangkan aplikasi mobile, dan Awesome Notifications adalah alat yang sangat kuat untuk mengimplementasikannya di Flutter. Dalam artikel ini, kita telah melihat langkah-langkah untuk mengintegrasikan Awesome Notifications ke dalam proyek Flutter Anda dan menciptakan notifikasi lokal yang menarik. Dengan pengetahuan ini, Anda dapat meningkatkan pengalaman pengguna dalam aplikasi Anda.

Tentu, Anda dapat menyesuaikan artikel ini sesuai dengan kebutuhan Anda, termasuk tambahan informasi atau penjelasan lebih rinci sesuai dengan pengalaman Anda dalam menggunakannya. Semoga artikel ini membantu pengembang Flutter dalam mengimplementasikan notifikasi lokal di aplikasi mereka.

LEAVE A REPLY

Please enter your comment!
Please enter your name here