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

Push Notification trong Flutter sử dụng Firebase Cloud Messaging.

0 0 33

Người đăng: Xuan Nghia

Theo Viblo Asia

Chào mọi người hôm này mình sẽ viết một bài về Push Notification trong một app mobile Flutter. Trước khi đi vào hướng dẫn mình sẽ nhắc lại một chút về Push Notification để gợi nhớ cho mọi người nhớ lại ?.

Push Notification là gì?

Push notification là những tin nhắn do server đẩy xuống client thông qua một cổng cloud message nào đó rồi từ cổng này sẽ thông báo xuống máy chúng ta là có thông báo mới và hiển thị chúng. Chính vì vậy nên phải có internet thì mới push notification được.

Tại sao sử dụng Push Notification

  • Thông thường một thông báo là được tự động kích hoạt nhằm thông báo tới người dùng là ứng dụng đó đã hoàn thành một công việc nào đó. Hoặc bạn có thể gửi thông tin khuyến mãi tới cho khách hàng của bạn, mời khách hàng tham gia một sự kiện nào đó... -
  • Khi xây dựng ứng dụng di động, việc push notification là một chức năng hẳn không thể bỏ qua, nó sẽ là cho ứng dụng của bạn trở nên (ngon) hơn.

Sơ qua là như vậy tiếp đến là phần chính của bài viết này, mọi người cùng tìm hiểu nhé.

Cài đặt Push Notification

Vì Flutter là một framework cross-platform nên ta sẽ phải cấu hình trên cả 2 file Android và iOS và ở đây mình chỉ cấu hình trên mobile thôi nhé. ?

Trước khi cài đặt Push Notification thì mọi người phải liến kết đến Firebase thì mới làm được phần này nhé.

Link tham khảo: https://viblo.asia/p/lien-ket-firebase-den-app-flutter-cho-android-va-ios-RnB5p6zdZPG

Cài đặt chung

Mọi người vào file pubspect.yaml thêm vào đoạn code bên dưới

dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 firebase_analytics: any firebase_messaging: 6.0.9 firebase_core: ^0.4.0

Sau đó gõ vào command line packages get để sync lại file config.

Cài đặt trên Android

Các bạn vào file android/build.gradle thêm vào đoạn sau:

 dependencies { classpath 'com.android.tools.build:gradle:3.5.0' classpath 'com.google.gms:google-services:4.3.3' }

Trong đường dẫn android/app/build.gradle apply plugin bên dưới

apply plugin: 'com.google.gms.google-services'

Để có thể xử lý message nền thì google đề nghị phải implement thêm theo phiên bản mới.

dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' implementation 'com.google.firebase:firebase-messaging:20.1.0'
}

Vào trong AndroidManifest.xml và add vào một intent như sau:

 <application ...> <activity android:name=".MainActivity" ...> ... <intent-filter> <action android:name="FLUTTER_NOTIFICATION_CLICK" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> ... </application>

Tạo thêm một class Application.java hoặc Application.kt(nếu mọi người dùng Kotlin nhé) trong thư mục có chứa MainActivity với nội dung sau:

public class Application extends FlutterApplication implements PluginRegistrantCallback { @Override public void onCreate() { super.onCreate(); FlutterFirebaseMessagingService.setPluginRegistrant(this); } @Override public void registerWith(PluginRegistry registry) { FirebaseCloudMessagingPluginRegistrant.registerWith(registry); }
}

Tạo thêm một class FirebaseCloudMessagingPluginRegistrant trong cùng thư mục với Application.java với nội dung sau.

import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin; public final class FirebaseCloudMessagingPluginRegistrant{ public static void registerWith(PluginRegistry registry) { if (alreadyRegisteredWith(registry)) { return; } FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin")); } private static boolean alreadyRegisteredWith(PluginRegistry registry) { final String key = FirebaseCloudMessagingPluginRegistrant.class.getCanonicalName(); if (registry.hasPlugin(key)) { return true; } registry.registrarFor(key); return false; }
}

Trong file MainActivity.java hãy comment nội dung của nó lại.

package com.djamware.myflutter; //import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
//import io.flutter.embedding.engine.FlutterEngine;
//import io.flutter.plugins.GeneratedPluginRegistrant; public class MainActivity extends FlutterActivity {
/* @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); }
}
*/

Cuối cùng là vào AndroidManifest.xml và đổi name của thẻ <application> như sau

<application android:name=".Application" ...
</application>

Thế là xong các bước cài đặt cho Android, bây giờ hãy sang iOS nào.

Cài đặt trên iOS

Mở file ios/Runner/AppDelegate.m hoặc ios/Runner/AppDelegate.swift và thêm dòng bên dưới vào bên trong (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method.

 if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate }

Code Demo

Để cho dễ dàng thì mình sẽ code trên file main.dart Mình sẽ tạo một phương thức để có thể nhận notification khi app chạy dưới background.

import 'dart:async'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart'; Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) { if (message.containsKey('data')) { final dynamic data = message['data']; } if (message.containsKey('notification')) { final dynamic notification = message['notification']; } }

Tiếp theo tạo một Map<String, Items>để có thể map với chuỗi JSON mà FCM trả về cho mình.

final Map<String, Item> _items = <String, Item>{};
Item _itemForMessage(Map<String, dynamic> message) { final dynamic data = message['data'] ?? message; final String itemId = data['id']; final Item item = _items.putIfAbsent(itemId, () => Item(itemId: itemId)) .._matchteam = data['matchteam'] .._score = data['score']; return item;
}

Tạo một Widget để hiển thị chi tiết message nhận được

class DetailPage extends StatefulWidget { DetailPage(this.itemId); final String itemId; @override _DetailPageState createState() => _DetailPageState();
} class _DetailPageState extends State<DetailPage> { Item _item; StreamSubscription<Item> _subscription; @override void initState() { super.initState(); _item = _items[widget.itemId]; _subscription = _item.onChanged.listen((Item item) { if (!mounted) { _subscription.cancel(); } else { setState(() { _item = item; }); } }); } @override Widget build(BuildContext context) { // This method is rerun every time setState is called, for instance as done // by the _incrementCounter method above. // // The Flutter framework has been optimized to make rerunning build methods // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets. return Scaffold( appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: Text("Match ID ${_item.itemId}"), ), body: SingleChildScrollView( child: Container( padding: EdgeInsets.all(20.0), child: Card( child: Container( padding: EdgeInsets.all(10.0), child: Column( children: <Widget>[ Container( margin: EdgeInsets.fromLTRB(0, 0, 0, 10), child: Column( children: <Widget>[ Text('Today match:', style: TextStyle(color: Colors.black.withOpacity(0.8))), Text( _item.matchteam, style: Theme.of(context).textTheme.title) ], ), ), Container( margin: EdgeInsets.fromLTRB(0, 0, 0, 10), child: Column( children: <Widget>[ Text('Score:', style: TextStyle(color: Colors.black.withOpacity(0.8))), Text( _item.score, style: Theme.of(context).textTheme.title) ], ), ), ], ) ), ), ), ), // This trailing comma makes auto-formatting nicer for build methods. ); }
}

Chỉnh sửa một chút về Homepage

class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); }
} class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState();
} class _MyHomePageState extends State<MyHomePage> { bool _topicButtonsDisabled = false; final FirebaseMessaging _firebaseMessaging = FirebaseMessaging(); final TextEditingController _topicController = TextEditingController(text: 'topic'); Widget _buildDialog(BuildContext context, Item item) { return AlertDialog( content: Text("${item.matchteam} with score: ${item.score}"), actions: <Widget>[ FlatButton( child: const Text('CLOSE'), onPressed: () { Navigator.pop(context, false); }, ), FlatButton( child: const Text('SHOW'), onPressed: () { Navigator.pop(context, true); }, ), ], ); } void _showItemDialog(Map<String, dynamic> message) { showDialog<bool>( context: context, builder: (_) => _buildDialog(context, _itemForMessage(message)), ).then((bool shouldNavigate) { if (shouldNavigate == true) { _navigateToItemDetail(message); } }); } void _navigateToItemDetail(Map<String, dynamic> message) { final Item item = _itemForMessage(message); // Clear away dialogs Navigator.popUntil(context, (Route<dynamic> route) => route is PageRoute); if (!item.route.isCurrent) { Navigator.push(context, item.route); } } @override void initState() { super.initState(); _firebaseMessaging.configure( onMessage: (Map<String, dynamic> message) async { print("onMessage: $message"); _showItemDialog(message); }, onBackgroundMessage: myBackgroundMessageHandler, onLaunch: (Map<String, dynamic> message) async { print("onLaunch: $message"); _navigateToItemDetail(message); }, onResume: (Map<String, dynamic> message) async { print("onResume: $message"); _navigateToItemDetail(message); }, ); _firebaseMessaging.requestNotificationPermissions( const IosNotificationSettings( sound: true, badge: true, alert: true, provisional: true)); _firebaseMessaging.onIosSettingsRegistered .listen((IosNotificationSettings settings) { print("Settings registered: $settings"); }); _firebaseMessaging.getToken().then((String token) { assert(token != null); print("Push Messaging token: $token"); }); _firebaseMessaging.subscribeToTopic("matchscore"); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('My Flutter FCM'), ), body: SingleChildScrollView( child: Container( padding: EdgeInsets.all(20.0), child: Card( child: Container( padding: EdgeInsets.all(10.0), child: Column( children: <Widget>[ Container( margin: EdgeInsets.fromLTRB(0, 0, 0, 10), child: Column( children: <Widget>[ Text('Welcome to this Flutter App:', style: TextStyle(color: Colors.black.withOpacity(0.8))), Text('You already subscribe to the matchscore topic', style: Theme.of(context).textTheme.title) ], ), ), Container( margin: EdgeInsets.fromLTRB(0, 0, 0, 10), child: Column( children: <Widget>[ Text('Now you will receive the push notification from the matchscore topics', style: TextStyle(color: Colors.black.withOpacity(0.8))) ], ), ), ], ) ), ), ), ), ); }
}
Finally, modify the main method to be like this. void main() { runApp( MaterialApp( home: MyHomePage(), ), );
}

Tạo một thông báo mới:

Nhấn vào Send Message và thêm token vào:

Cách để lấy token:

 _firebaseMessaging.getToken().then((String token) { assert(token != null); print("Push Messaging token: $token"); });

Thiết lập data trên Clound Messaging như sau:

Tiến hành build và test. Ở màn hình Home sẽ như sau:

Khi nhận được dữ liệu sẽ hiển thị lên dialog.

Và khi app chạy dưới nền

Cảm ơn mọi người đã theo dõi.

Link tham khảo :

https://pub.dev/packages/firebase_messaging

https://www.djamware.com/post/5e4b26e26cdeb308204b427f/flutter-tutorial-firebase-cloud-messaging-fcm-push-notification

Github : https://github.com/didinj/flutter-firebase-cloud-messaging-fcm-push-notification-example.git

Bình luận

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

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

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 500

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

Cài đặt WSL / WSL2 trên Windows 10 để code như trên Ubuntu

Sau vài ba năm mình chuyển qua code trên Ubuntu thì thật không thể phủ nhận rằng mình đã yêu em nó. Cá nhân mình sử dụng Ubuntu để code web thì thật là tuyệt vời.

0 0 374

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

Đặt tên commit message sao cho "tình nghĩa anh em chắc chắn bền lâu"????

. Lời mở đầu. .

1 1 701

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

Tìm hiểu về Resource Controller trong Laravel

Giới thiệu. Trong laravel, việc sử dụng các route post, get, group để gọi đến 1 action của Controller đã là quá quen đối với các bạn sử dụng framework này.

0 0 335

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

Phân quyền đơn giản với package Laravel permission

Như các bạn đã biết, phân quyền trong một ứng dụng là một phần không thể thiếu trong việc phát triển phần mềm, dù đó là ứng dụng web hay là mobile. Vậy nên, hôm nay mình sẽ giới thiệu một package có thể giúp các bạn phân quyền nhanh và đơn giản trong một website được viết bằng PHP với framework là L

0 0 421

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

Bạn đã biết các tips này khi làm việc với chuỗi trong JavaScript chưa ?

Hi xin chào các bạn, tiếp tục chuỗi chủ đề về cái thằng JavaScript này, hôm nay mình sẽ giới thiệu cho các bạn một số thủ thuật hay ho khi làm việc với chuỗi trong JavaScript có thể bạn đã hoặc chưa từng dùng. Cụ thể như nào thì hãy cùng mình tìm hiểu trong bài viết này nhé (go).

0 0 414