- vừa được xem lúc

Awesome Notifications - Bài 2: Tùy biến giao diện trên thanh thông báo.

0 0 20

Người đăng: Anh Quang

Theo Viblo Asia

Tiếp tục bài viết tiếp theo trong series Awesome Notifications. Hôm nay mình sẽ mang đến cho các bạn cách tùy biến giao diện trên thanh thông báo sao cho bắt mắt nhé!

Note: Vì mình chưa chuẩn bị kịp tài khoản cá nhân Apple Developer Account (để push notification) nên series này mình test trên thiết bị Android nha. 🥲

1. Tại sao cần tùy biến giao diện Notification?

Thật ra việc tùy biến UI của Notification tùy thuộc vào yêu cầu, mục đích của project. Ở bài trước mình đã biết về khái niệm Rich Notification. Gửi image là ví dụ dễ thấy nhất của Rich Notification. Ngoài ra Rich Notification còn có nhiều tùy biến khác như: các nút CTA (Call to action), thanh text field,.. Những kiểu tùy biến này các bạn có thể dễ dàng nhận thấy trên các ứng dụng như: Messenger, Spotify, ZingMP3,..

2. Emojis

Thêm các emojis vào thanh thông báo cho bớt nhàm chán 🤩.

  • Đối với Local Notification: dùng class Emoji đã hỗ trợ sẵn để concat vào text như ví dụ sau:
    { title: 'Emojis are awesome too! '+ Emojis.smille_face_with_tongue + Emojis.smille_rolling_on_the_floor_laughing + Emojis.emotion_red_heart,
    }
    

hoặc dùng unicode text với format sau \u{1f6f8}

  • Đối với Push notification:

    • Copy thẳng icon từ link này rồi paste vào postman như sau:
    "title": "Android! The eagle has landed! 😀",
    
    • Dùng mã Dec/Hex từ link này, ví dụ: &#129319
    "title": "Android! The eagle has landed! &#129319",
    

3. Notification Layout Types

Dùng để quy định layout nào bạn muốn dùng cho từng mục đích cụ thể, gồm các loại sau:

  • Default: Mặc định.
  • BigPicture: hiển thị ảnh lớn và/hoặc ảnh nhỏ đính kèm với thông báo.
  • BigText: hiển thị hơn 2 dòng văn bản.
  • Inbox: liệt kê các thư hoặc mục được phân tách với nhau bằng dòng.
  • ProgressBar: hiển thị thanh tiến trình, ví dụ thanh tiến trình khi bạn download hình ảnh cho app chẳng hạn (thường thấy trong telegram,..).
  • Messaging: hiển thị từng thông báo dưới dạng cuộc trò chuyện trò chuyện 1 vs 1.
  • Messaging Group: hiển thị mỗi thông báo dưới dạng một cuộc trò chuyện nhóm.
  • MediaPlayer: hiển thị bộ điều khiển phương tiện có các nút tác vụ, cho phép user thao tác mà không đưa ứng dụng lên foreground. (thường thấy trên Spotify, ZingMp3, Youtube Music..)

Mình sẽ thử 1 vài ví dụ cho các bạn dễ hình dung nhé. ✌️

Gọi các sự kiện ở màn hình Home (file main.dart):

 Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ ElevatedButton( onPressed: () { notifController.localNotification(); }, child: const Icon(Icons.circle_notifications), ), ElevatedButton( onPressed: () { notifController.localDownload(); }, child: const Icon(Icons.download), ), ElevatedButton( onPressed: () { notifController.localMediaNotif(); }, child: const Icon(Icons.music_video), ), ], ), ), ); }

BigPicture

Giống như ví dụ đã demo ở Bài 1 mình sẽ config thêm 1 nút reply để reply tin nhắn nhận về. Ví dụ được sử dụng trong các app như Messenger, Zalo, Telegram,..

Future<void> localNotification() async { await AwesomeNotifications().createNotification( content: NotificationContent( id: 1, channelKey: 'alerts', // Demo cái Emojis nè title: 'Emojis are awesome too! ' + Emojis.activites_admission_tickets + Emojis.activites_balloon + Emojis.emotion_red_heart, body: "Emojis awesome body", // Gắn hình vào thông báo ở đây nè bigPicture: 'https://images.pexels.com/photos/14679216/pexels-photo-14679216.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', notificationLayout: NotificationLayout.BigPicture, ), actionButtons: [ // Thêm nút NotificationActionButton để reply tin nhắn trên thanh thông báo  NotificationActionButton( key: 'REPLY', label: 'Reply Message', requireInputText: true, actionType: ActionType.SilentAction, ), // Nút này để Dismiss cái notification thôi :v NotificationActionButton( key: 'DISMISS', label: 'Dismiss', autoDismissible: true, ), ]); }

ProgressBar

Thêm 1 ví dụ về NotificationLayoutProgressBar. Thường dùng để hiển thị tiến trình download hình ảnh, hay video (có thể dễ dàng bắt gặp ở Telegram,..).

Future<void> localDownload() async { await AwesomeNotifications().createNotification( content: NotificationContent( id: 1, channelKey: 'alerts', title: 'Downloading...', bigPicture: 'https://images.pexels.com/photos/14679216/pexels-photo-14679216.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', // Đổi cái notificationLayout thành ProgressBar để sử dụng nhé notificationLayout: NotificationLayout.ProgressBar, // Có thể dùng process để hiện tốc độ tải progress: 70, // thay thế giá trị cần thiết ở đây nha ), ); }

MediaPlayer

Hiển thị thanh trạng thái, điều khiển cho ứng dụng nghe nhạc, xem phim thử xem nào 🥰

Future<void> localMediaNotif() async { await AwesomeNotifications().createNotification( content: NotificationContent( id: 1, channelKey: 'alerts', title: 'Đã sai từ lúc đầu', body: "Bùi Anh Tuấn ft Trung Quân", // Dùng MediaPlayer cho notificationLayout notificationLayout: NotificationLayout.MediaPlayer, summary: 'Now playing', largeIcon: 'https://avatar-ex-swe.nixcdn.com/song/2022/08/16/f/3/3/4/1660622960262_640.jpg', ), actionButtons: [ // Thêm các nút để điều khiển trình nghe nhạc như prev, pause, next NotificationActionButton( key: 'MEDIA_PREV', // thêm icon cho các nút ở đây icon: 'resource://drawable/previous', label: 'Previous', showInCompactView: false, enabled: true, actionType: ActionType.SilentAction, ), NotificationActionButton( key: 'MEDIA_PAUSE', icon: 'resource://drawable/pause', label: 'Pause', showInCompactView: false, enabled: true, actionType: ActionType.SilentAction, ), NotificationActionButton( key: 'MEDIA_NEXT', icon: 'resource://drawable/next', label: 'Next', showInCompactView: false, enabled: true, actionType: ActionType.SilentAction, ), ]); }

Lưu ý: icon trên Android được lưu ở đường dẫn “[project]/android/app/src/main/drawable”. Ví dụ: resource://drawable/res_image-asset.png

4. Handle sự kiện nhận được khi nhấn các button trên thanh thông báo.

Ở file main.dart thêm function startListeningNotificationEvents để lắng nghe các sự kiện trả về:

class _MyAppState extends State<MyApp> { final notifController = NotificationController(); // This widget is the root of your application.  void initState() { notifController.checkPermission(); notifController.requestFirebaseToken(); // Gọi function startListeningNotificationEvents notifController.startListeningNotificationEvents(); super.initState(); }  Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), builder: EasyLoading.init(), ); }
}

Ở file notification/notification_controller.dart: thêm function để lắng nghe các sự kiện trả về

Future<void> startListeningNotificationEvents() async { AwesomeNotifications().setListeners( onActionReceivedMethod: onActionReceivedMethod, ); } ('vm:entry-point') static Future<void> onActionReceivedMethod( ReceivedAction receivedAction) async { if (receivedAction.actionType == ActionType.SilentAction || receivedAction.actionType == ActionType.SilentBackgroundAction) { // Lấy tin nhắn đã nhập vào ô input khi nhấn nút reply debugPrint('---Input message---${receivedAction.buttonKeyInput}------'); // Handle các sự kiện media player switch (receivedAction.buttonKeyPressed) { case 'MEDIA_PREV': // Handle media prev debugPrint("-----------MEDIA_PREV-----------"); break; case 'MEDIA_PAUSE': // Handle media pause debugPrint("-----------MEDIA_PAUSE-----------"); break; case 'MEDIA_NEXT': // Handle media next debugPrint("-----------MEDIA_NEXT-----------"); break; } } else { EasyLoading.showToast("FCM message ne!", toastPosition: EasyLoadingToastPosition.bottom); } }

5. Với Push notification thì như thế nào nhỉ?

Những ví dụ trên là cho trường hợp Local Notification, Vậy, với push notification thì chúng ta có thể tham khảo format chuẩn sau:

{ "to" : "Paste FCM token vào đây nha!", "priority": "high", "content_available": true, "notification": { "badge": 42, "title": "Huston! The eagle has landed!", "body": "Bacon ipsum dolor amet bacon prosciutto chislic pork belly bresaola pig jerky" }, "data" : { "content": { "id": 1, "badge": 42, "channelKey": "alerts", "displayOnForeground": true, "notificationLayout": "BigPicture", "largeIcon": "https://images.pexels.com/photos/3921014/pexels-photo-3921014.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2", "bigPicture": "https://images.pexels.com/photos/14679216/pexels-photo-14679216.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2", "showWhen": true, "autoDismissible": true, "privacy": "Private", "payload": { "secret": "Awesome Notifications Rocks!" } }, "actionButtons": [ { "key": "REPLY", "label": "Reply", "requireInputText": true }, { "key": "DISMISS", "label": "Dismiss", "actionType": "DismissAction", "isDangerousOption": true, "autoDismissible": true } ], "Android": { "content": { "title": "Android! Notification title &#129319", // Gắn cái emojis vào nè! "payload": { "android": "android custom content!" } } }, "iOS": { "content": { "title": "Jobs! The eagle has landed!", "payload": { "ios": "ios custom content!" } }, "actionButtons": [ { "key": "REDIRECT", "label": "Redirect message", "autoDismissible": true }, { "key": "DISMISS", "label": "Dismiss message", "actionType": "DismissAction", "isDangerousOption": true, "autoDismissible": true } ] } }
}

cURL trên postman cho bạn nào cần

curl --location 'https://fcm.googleapis.com/fcm/send' \
--header 'Content-Type: application/json' \
--header 'Authorization: key=AAAAgCbszqQ:APA91bEroOcx2-hdno1n7QH4elwA_S4WpcBX8nsqJPtvigWUk824wGyEovL9mpE3EynZtrAYdDdAKesvE1KoFScmF97eeRsshVMCbFHEwc296cquuOhPDbThN9ByLuBttIyTC_XzqHKA' \
--data '{ "to" : "dzia1myTSLKf1TsRKq57EE:APA91bEAi-ihDZzvSCK3LYNg2eVTNM94T4_BeVSIhL3aLvIin0SkZCMn_3WHGyETk-revWUMnD8X6xxWrIpZFgjF9ZJAaAFDjb9TDYAZHPPLxdFi3RNtWjDZitBHc9FYCufn28DiPN63", "priority": "high", "content_available": true, "notification": { "badge": 42, "title": "Huston! The eagle has landed!", "body": "Bacon ipsum dolor amet bacon prosciutto chislic pork belly bresaola pig jerky" }, "data" : { "content": { "id": 1, "badge": 42, "channelKey": "alerts", "displayOnForeground": true, "notificationLayout": "BigPicture", "largeIcon": "https://images.pexels.com/photos/3921014/pexels-photo-3921014.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2", "bigPicture": "https://images.pexels.com/photos/14679216/pexels-photo-14679216.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2", "showWhen": true, "autoDismissible": true, "privacy": "Private", "payload": { "secret": "Awesome Notifications Rocks!" } }, "actionButtons": [ { "key": "REPLY", "label": "Reply", "requireInputText": true }, { "key": "DISMISS", "label": "Dismiss", "actionType": "DismissAction", "isDangerousOption": true, "autoDismissible": true } ], "Android": { "content": { "title": "Android! Notification title &#129319", "payload": { "android": "android custom content!" } } }, "iOS": { "content": { "title": "Jobs! The eagle has landed!", "payload": { "ios": "ios custom content!" } }, "actionButtons": [ { "key": "REDIRECT", "label": "Redirect message", "autoDismissible": true }, { "key": "DISMISS", "label": "Dismiss message", "actionType": "DismissAction", "isDangerousOption": true, "autoDismissible": true } ] } }
}'

6. Tổng kết

Bài vừa rồi, chúng ta đã tìm hiểu được những tiện ích mà Awesome Notifications mang lại như có thể tùy biết layout theo những nhu cầu, ứng dụng khác nhau. Ở bài viết tiếp theo mình sẽ tìm hiểu sâu hơn về cách hoạt động của push notification. Cùng đón đọc những bài viết tiếp theo trong series này nhé! 🤩

7. Tài liệu tham khảo

[https://pub.dev/packages/awesome_notifications_fcm/] https://pub.dev/packages/awesome_notifications

8. Source code

https://github.com/AnhQuangCee/flutter-research/tree/awesome-notif/lesson-2

Bình luận

Bài viết tương tự

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 1: Làm quen cô nàng Flutter

Lời mở đầu. Gần đây, Flutter nổi lên và được Google PR như một xu thế của lập trình di động vậy.

0 0 254

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 3: Lột trần cô nàng Flutter, BuildContext là gì?

Lời mở đầu. Màn làm quen cô nàng FLutter ở Phần 1 đã gieo rắc vào đầu chúng ta quá nhiều điều bí ẩn về nàng Flutter.

0 0 189

- vừa được xem lúc

[Android] Hiển thị Activity trên màn hình khóa - Show Activity over lock screen

Xin chào các bạn, Hôm nay là 30 tết rồi, ngồi ngắm trời chờ đón giao thừa, trong lúc rảnh rỗi mình quyết định ngồi viết bài sau 1 thời gian vắng bóng. .

0 0 93

- vừa được xem lúc

Tìm hiểu Proguard trong Android

1. Proguard là gì . Cụ thể nó giúp ứng dụng của chúng ta:. .

0 0 82

- vừa được xem lúc

Làm ứng dụng học toán đơn giản với React Native - Phần 6

Chào các bạn một năm mới an khang thịnh vượng, dồi dào sức khỏe. Lại là mình đây Đây là link app mà các bạn đang theo dõi :3 https://play.google.com/store/apps/details?id=com.

0 0 51

- vừa được xem lúc

20 Plugin hữu ích cho Android Studio

1. CodeGlance. Plugin này sẽ nhúng một minimap vào editor cùng với thanh cuộn cũng khá là lớn. Nó sẽ giúp chúng ta xem trước bộ khung của code và cho phép điều hướng đến đoạn code mà ta mong muốn một cách nhanh chóng.

0 0 301