Navigate to a View on FCM Notification Click


What is FCM

As for the Wikipedia,

Firebase Cloud Messaging, formerly known as Google Cloud Messaging, is a cross-platform cloud solution for messages and notifications for Android, iOS, and web applications

I think that explains the whole purpose of using FCM. Even most of the AWS users use FCM for their messaging and notification needs, just for the easiness that brings to the developer. In addition, lately people trend to use data notifications to replace socket programming that was using mainly for realtime updates. Most of all, it is a completely free service, at least for now. So most of the startups, small businesses can benefit from that service.

Setting up FCM in flutter is not a hard thing. Anyone with an IDE can do it just by reading their documentation. If you haven’t done it to your project, follow the below link and setup your Flutter app for FCM.

Firebase Cloud Messaging for Flutter

Follow the instructions in this documentation to set up FCM in your flutter app

pub.dev

Pre Requirements

To move forward in this article, simple knowledge in BLoC pattern would be recommended. If you need a quick go though, you can refer the link below. For this project, I will be using the BLoC implementation by @Felangel.

Bloc library documentation

Have a quick go through in this documentation to learn some basic concepts behind BLoC

bloclibrary.dev

In addition, you should have a pre configured project for FCM. Also, knowledge about Flutter navigation is an essential.

Setting up Views

For this, I will create an app with 2 view. One will have a list of documents from a Firestore collection. When you click on a list item, it will navigate to the second view and show the information in the document.

First I will talk about the second view. It should take a DocumentReference as a parameter and show it’s detials in its view. For that purpose, I will directly use this article I wrote.

How to use Future Builders Effectively

This article will discuss more about using Futures with FutureBuilders. But the code can be used for this article as well to view the data. You can directly goto the code at the bottom or read the article and understand how it works.

www.fcodelabs.com

Then for the first page, I will create a ListView to show the list of DocumentReferences and, when you click one of them, it will navigate to the second page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void navigateToSecondView(BuildContext context, DocumentReference ref) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(ref: ref),
));
}

final refs = <DocumentReference>[];

@override
Widget build(BuildContext context) {
// ... Parent Widget
child: ListView.builder(
itemCount: refs.length,
itemBuilder: (context, i) =>
ListTile(
title: Text(refs[i].path),
onTap: () => navigateToSecondView(context, refs[i]),
),
),
}

Setting Up the BLoC

For this BLoC, you will need an Event to add DocumentReferences and, the BLoC’s state should tell us whether we need to navigate to the SecondPage or not. The following code must be straight forward if you have a basic knowledge to use the above mentioned implementation of BLoC.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class UtilEvent {}

class NavigateToView extends UtilEvent {
final String path;

NavigateToView(this.path);
}

class UtilState {}

class NavigateToSecond extends UtilState {
final DocumentReference ref;

NavigateToSecond(this.ref);
}

class NoNavigation extends UtilState {}

class UtilBloc extends Bloc<UtilEvent, UtilState> {
@override
UtilState get initialState => NoNavigation();

@override
Stream<UtilState> mapEventToState(UtilEvent event) async* {
// Event to State mapping
}
}

Adding Actions

When sending notification, you have to use the extra parameter userRef with the data that is sending. That parameter should contain the path to the document that should show in the SecondPage when clicked on the notification. Take a look at the following article to learn how to use Postman to send test notifications to the app. Make sure to add userRef variable with a correct path to a document in a Firestore collection.

Send Firebase push notifications using Postman

This article will explain on how to test your FCM push notifications with Postman

www.fcodelabs.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
final _firebaseMessaging = FirebaseMessaging();

// Setup FCM token and do other stuff

_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print('on message $message');
},
onResume: (Map<String, dynamic> message) async {
print('on resume $message');
final path = message['data']['userRef'];
add(NavigateToView(path));
},
onLaunch: (Map<String, dynamic> message) async {
print('on launch $message');
final path = message['data']['userRef'];
add(NavigateToView(path));
},
);

Once the event was added, it should be processed in the UtilBloc and set the appropriate state.

1
2
3
4
5
6
7
8
9
10
11
12
13
@override
Stream<UtilState> mapEventToState(UtilEvent event) async* {
switch (event.runtimeType) {
case NavigateToView:
final e = event as NavigateToView;
if (e.path?.isEmpty ?? true) {
yield NoNavigation();
break;
}
yield NavigateToSecond(Firestore.instance.document(e.path));
break;
}
}

This will set the state to NoNavigation when the event received with an empty or null path to a Firestore document. But if there is a value in the path, it should set the state to NavigateToSecond with the relevant DocumentReference.

Handling the Navigation

You can use the method in the article that mentioned above to trigger manual notifications to the app. When you click on a notification, you will see that the app is starting normally and going to the home page. Also, you will see that “on resume” or “on launch” statements are printed in the console. What is remaining is to trigger a navigation to the SecondPage by looking at the state of this BLoC at the home page of our app.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@override
Widget build(BuildContext context) {
final bloc = BlocProvider.of<UtilBloc>(context);

return BlocBuilder<UtilBloc, UtilState>(
builder: (context, state) {
if (state is NavigateToSecond) {
bloc.add(NavigateToView(null));
SchedulerBinding.instance.addPostFrameCallback((_) {
navigateToSecondView(context, state.ref);
});
}
return Scaffold(
body: Center(
child: ListView.builder(
...
),
),
);
}
);
}

Before actually navigating to the second view, you should reset the state of the BLoC so that the BlocBuilder will not execute the navigation code on every time it rebuilds. For other ways of handling this navigation, you can refer to this github issue and this article in the bloc documentation.

You can goto the following link to view the whole code.

Gist Containing all the Code

You can goto this link to view all the code that I discussed here, in a single gist.

gist.github.com

I don’t say that this is the only way of handling navigation on notification click in Flutter. There might be hundreds of ways, which will even better than this. If you came across a better implementation by yourself, or you saw a better approach on the internet, just leave a comment so that I also can learn it.