4

I'm trying to create notification badge inside application (in home screen) on this code and I'm using Firebase Cloud Messaging with android app in flutter language, problem is I can't figure out how to count the number of received notification so

Any suggested way to count the number of received notifications from Firebase Cloud Messaging to android app?

PS: i have updated code now for the answer below and i'm still getting errors

// import 'package:flutter/foundation.dart';
// import 'package:flappy_search_bar/flappy_search_bar.dart';
import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:mycafe/main.dart';
import 'Custom_Text.dart';
import 'Pasta.dart';
import 'Burger.dart';
import 'Pizza.dart';
import 'AboutUs.dart';
import 'dart:async';
import 'ui/home/HomeScreen.dart';
import 'dart:math';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'ContactUs.dart';
import 'package:flutter/services.dart';
import 'package:mycafe/model/User.dart';
import 'package:mycafe/ui/home/HomeScreen.dart';
import 'package:mycafe/ui/services/Authenticate.dart';
import 'package:mycafe/ui/utils/helper.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
import 'dart:io';
import 'package:flushbar/flushbar.dart';
import 'package:flushbar/flushbar_helper.dart';
// import 'package:flutter/material.dart';

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

import 'constants.dart' as Constants;
import 'ui/auth/AuthScreen.dart';
import 'ui/onBoarding/OnBoardingScreen.dart';

import 'package:flutter/cupertino.dart';

import 'package:mycafe/ui/auth/AuthScreen.dart';

var bannerItems = ["Burger", "cheesechilly", "Noodles", "Pizza"];
var bannerImages = [
  "images/burger.jpg",
  "images/cheesechilly.jpg",
  "images/noodles.jpg",
  "images/pizza.jpg"
];

ValueNotifier<int> notificationCounterValueNotifer = ValueNotifier(0);

class Notify extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlutterBase',
      home: Scaffold(
        body: MessageHandler(),
      ),
    );
  }
}

class MessageHandler extends StatefulWidget {
  @override
  _MessageHandlerState createState() => _MessageHandlerState();
}

class _MessageHandlerState extends State<MessageHandler> with ChangeNotifier {
  final Firestore _db = Firestore.instance;
  final FirebaseMessaging _fcm = FirebaseMessaging();

  StreamSubscription iosSubscription;

  @override
  void initState() {
    super.initState();
    if (Platform.isIOS) {
      iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
        print(data);
        _saveDeviceToken();
      });

      _fcm.requestNotificationPermissions(IosNotificationSettings());
    } else {
      _saveDeviceToken();
    }

// void _incrementCounter() {
    _fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");

        // RaisedButton(
        //   child: Text(message['notification']['title']),
        //   onPressed: () {
        //     Flushbar(
        //       flushbarPosition: FlushbarPosition.TOP,
        //       icon: Icon(
        //         Icons.notifications_active,
        //         color: Colors.white,
        //       ),
        //       mainButton: FlatButton(
        //         onPressed: () {
        //           Navigator.pop(context);
        //           //  Flush.showGoodFlushbar(context, 'login successful!');
        //         },
        //         // child: Text(
        //         //   "ADD",
        //         //   style: TextStyle(color: Colors.amber),
        //         // ),
        //       ),
        //       // duration: Duration(seconds: 7))
        //     ).show(context);
        //   },
        // );
        notificationCounterValueNotifer.value++;
        notificationCounterValueNotifer
            .notifyListeners(); // notify listeners here so ValueListenableBuilder will build the widget.
        final snackbar = SnackBar(
          content: Text(message['notification']['title']),
          action: SnackBarAction(
            label: 'Go',
            onPressed: () => null,
          ),
        );

        Scaffold.of(context).showSnackBar(snackbar);
        // showDialog(
        //   context: context,
        //   builder: (context) => AlertDialog(
        //     content: ListTile(
        //       title: Text(message['notification']['title']),
        //       subtitle: Text(message['notification']['body']),
        //     ),
        //     actions: <Widget>[
        //       FlatButton(
        //         color: Colors.amber,
        //         child: Text('Ok'),
        //         onPressed: () => Navigator.of(context).pop(),
        //       ),
        //     ],
        //   ),
        // );
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
        // TODO optional
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
        // TODO optional
      },
    );
  }

  @override
  void dispose() {
    if (iosSubscription != null) iosSubscription.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // _handleMessages(context);
    return MaterialApp(home: Scaffold(body: HomeApp()));
  }

  /// Get the token, save it to the database for current user
  _saveDeviceToken() async {
    // Get the current user
    String uid = 'jeffd23';
    // FirebaseUser user = await _auth.currentUser();

    // Get the token for this device
    String fcmToken = await _fcm.getToken();

    // Save it to Firestore
    if (fcmToken != null) {
      var tokens = _db
          .collection('users')
          .document(uid)
          .collection('tokens')
          .document(fcmToken);

      await tokens.setData({
        'token': fcmToken,
        'createdAt': FieldValue.serverTimestamp(), // optional
        'platform': Platform.operatingSystem // optional
      });
    }
  }

  /// Subscribe the user to a topic
  _subscribeToTopic() async {
    // Subscribe the user to a topic
    _fcm.subscribeToTopic('puppies');
  }
}

Widget myAppBarIcon() {
  if (State is ValueNotifier) {
    return ValueListenableBuilder(
      builder: (BuildContext context, int newNotificationCounterValue,
          Widget child) {
        // return Container(
        // width: 50,
        // height: 10,

        child:
        Stack(
          children: [
            Icon(
              Icons.notifications,
              color: Colors.white,
              size: 30,
            ),
            Container(
              width: 30,
              height: 30,
              alignment: Alignment.topRight,
              margin: EdgeInsets.only(top: 2),
              child: Container(
                width: 15,
                height: 15,
                decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    color: Color(0xffc32c37),
                    border: Border.all(color: Colors.white, width: 1)),
                child: Padding(
                  padding: const EdgeInsets.all(0.0),
                  child: Text(
                    newNotificationCounterValue.toString(),
                  ),
                ),
              ),
            ),
          ],
        );

        //return your badge here
      },
      valueListenable: notificationCounterValueNotifer,
    );
    // return Container(
    //   width: 50,
    //   height: 10,
    //   child: Stack(
    //     children: [
    //       Icon(
    //         Icons.notifications,
    //         color: Colors.white,
    //         size: 30,
    //       ),
    //       Container(
    //         width: 30,
    //         height: 30,
    //         alignment: Alignment.topRight,
    //         margin: EdgeInsets.only(top: 2),
    //         child: Container(
    //           width: 15,
    //           height: 15,
    //           decoration: BoxDecoration(
    //               shape: BoxShape.circle,
    //               color: Color(0xffc32c37),
    //               border: Border.all(color: Colors.white, width: 1)),
    //           child: Padding(
    //             padding: const EdgeInsets.all(0.0),
    //             child: Center(
    //               child: ValueListenableBuilder(),
    //             ),
    //           ),
    //         ),
    //       ),
    //     ],
    //   ),
    // );
  } else {
    return Container();
  }
}

class HomeApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.deepOrange[900],
        title: Text('HuQQa BuzZ'),
        actions: <Widget>[myAppBarIcon()],
Christopher Moore
  • 15,626
  • 10
  • 42
  • 52
  • 2
    You can create a global variable called `notificationCounter` and whenever you receive a new notification, increase that number by 1. You can set it to 0 when you open the notification screen. Finally, store it in a firebase document so that next time you start the app, you get the number of notifications from your database. – Morez Sep 09 '20 at 13:22
  • @ Morez what if i want to keep all my notification in my app? – Merym Sep 09 '20 at 15:11
  • @Morez actually i'm trying to do that but i'm failing with it, i would be happy if u can help –  Sep 09 '20 at 17:16

2 Answers2

2

This is how I do it:

//define a global variable
ValueNotifier<int> notificationCounterValueNotifer =
    ValueNotifier(0); 

You can read about ValueNotifier here.

Then when you receive a new notification, increase that value by 1:

 _fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        notificationCounterValueNotifer.value++;
        notificationCounterValueNotifer.notifyListeners(); // notify listeners here so ValueListenableBuilder will build the widget.
        

        // final snackbar = SnackBar(
        //   content: Text(message['notification']['title']),
        //   action: SnackBarAction(
        //     label: 'Go',
        //     onPressed: () => null,
        //   ),
        // );

        // Scaffold.of(context).showSnackBar(snackbar);
        showDialog(
          context: context,
          builder: (context) => AlertDialog(
                content: ListTile(
                  title: Text(message['notification']['title']),
                  subtitle: Text(message['notification']['body']),
                ),
                actions: <Widget>[
                  FlatButton(
                    color: Colors.amber,
                    child: Text('Ok'),
                    onPressed: () => Navigator.of(context).pop(),
                  ),
                ],
              ),
        );
      },
);

In order to notify listeners, you have to add ChangeNotifer as a mixin to your class:

class _MessageHandlerState extends State<MessageHandler> with ChangeNotifier

You can choose a ValueListenableBuilder to build widgets whenever the value changes. so to update the badge, we use this widget:

 ValueListenableBuilder(
              builder: (BuildContext context, int newNotificationCounterValue, Widget child) {
                // This builder will only get called when the notificationCounterValueNotifer is updated.
                return Text(newNotificationCounterValue.toString()); //return your badge here
              },
              valueListenable: notificationCounterValueNotifer,
);

You can store the value in a database document and assign it at the start of your app so your notificationCounterValueNotifer value is not 0.

If you want, you can set it to 0 again when you navigate to a notificationScreen.

There might be better ways to do this but this is how I do it.

Update: You need to return ValueListenableBuilder from myAppBarIcon:

Widget myAppBarIcon() {
    //you have to return the widget from builder method.
    //you can add logics inside your builder method. for example, if you don't want to show a badge when the value is 0.
    return ValueListenableBuilder(
      builder: (BuildContext context, int newNotificationCounterValue,
          Widget child) { 
        //returns an empty container if the value is 0 and returns the Stack otherwise
        return  newNotificationCounterValue == 0? Container(): Stack(
          children: [
            Icon(
              Icons.notifications,
              color: Colors.white,
              size: 30,
            ),
            Container(
              width: 30,
              height: 30,
              alignment: Alignment.topRight,
              margin: EdgeInsets.only(top: 2),
              child: Container(
                width: 15,
                height: 15,
                decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    color: Color(0xffc32c37),
                    border: Border.all(color: Colors.white, width: 1)),
                child: Padding(
                  padding: const EdgeInsets.all(0.0),
                  child: Text(
                    newNotificationCounterValue.toString(),
                  ),
                ),
              ),
            ),
          ],
        );
      },
      valueListenable: notificationCounterValueNotifer,
    );
}
Morez
  • 2,085
  • 2
  • 10
  • 33
  • thank you very much but still getting error , i have updated post depending on your answer please can you check it for me –  Sep 09 '20 at 20:31
  • I updated my answer. You need to notifyListeners when you change the value of the ValueNotifier. Can you also tell me what is the error you’re getting? – Morez Sep 09 '20 at 22:56
  • error is ( Row's children must not contain any null values, but a null value was found at index 0), and it's still showing :/ –  Sep 10 '20 at 00:24
  • You have to return `ValueListenableBuilder` from `myAppBarIcon` because `ValueListenableBuilder` is a widget but `myAppBarIcon` is not returning it – Morez Sep 10 '20 at 00:59
  • i did another edit to the post , i used if else inorder to avoid null value because it needs another return value (when it's null) i have no errors now but the problem is : it's not showing the notification icon on the app bar as its returning only null.However, i tried to send notifications from FCM but icon still not showing –  Sep 10 '20 at 02:08
  • Only return the value listenable builder from the function and add your logic inside the builder method. So if you don’t want to show any badge when there is no notification, return an empty container inside the builder method of value listenable builder. You don’t see anything because when the app starts, the function returns an empty container and your widget tree will stay the same even though you the value notifier value changes – Morez Sep 10 '20 at 02:19
  • I would be thankful if you please update the answer depending on what you said –  Sep 10 '20 at 08:03
  • Thank you very much!! this is the best way –  Sep 10 '20 at 12:26
  • Happy to help. If you found any better solutions please let me know as well – Morez Sep 10 '20 at 12:37
  • @ Morez what if i want to keep all my notification in my app? – Merym Sep 11 '20 at 09:56
  • 1
    The data will be lost when you close the app. You can save that number on the device by using `Shared_Preferences` package: https://pub.dev/packages/shared_preferences . Read about the package and look at the examples to learn how to use it – Morez Sep 11 '20 at 11:38
  • @Morez i used your answer in my app its work but i get _NotificationScreenState.dispose failed to call super.dispose., The ValueNotifier sending notification was: I/flutter ( 7213): ValueNotifier#3e083(0) when i remove changeNootifier i dont see this error have an idae please – Merym Sep 17 '20 at 10:12
  • I also receive the same error and I found this: https://stackoverflow.com/questions/48678788/flutter-screenstate-dispose-method-exception and this: https://github.com/flutter/flutter/issues/24293 – Morez Sep 17 '20 at 11:54
0

You can use:

List<ActiveNotification> activeNotification = await fln.getActiveNotifications();
Dr Mido
  • 2,414
  • 4
  • 32
  • 72
Mehdi
  • 1